From 95b5c653a2ff05221291c9da9203f3587828a87c Mon Sep 17 00:00:00 2001 From: Jeshurun Hembd Date: Fri, 20 Sep 2024 00:14:07 -0400 Subject: [PATCH 001/175] Gaussian-weighted ambient occlusion with distance-dependent variance --- .../AmbientOcclusionGenerate.glsl | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/packages/engine/Source/Shaders/PostProcessStages/AmbientOcclusionGenerate.glsl b/packages/engine/Source/Shaders/PostProcessStages/AmbientOcclusionGenerate.glsl index 42535230cbdc..2b4d44495a98 100644 --- a/packages/engine/Source/Shaders/PostProcessStages/AmbientOcclusionGenerate.glsl +++ b/packages/engine/Source/Shaders/PostProcessStages/AmbientOcclusionGenerate.glsl @@ -40,6 +40,13 @@ vec3 getNormalXEdge(vec3 positionEC) return normalize(cross(dx, dy)); } +const float sqrtTwoPi = sqrt(czm_twoPi); + +float gaussian(float x, float standardDeviation) { + float argument = x / standardDeviation; + return exp(-0.5 * argument * argument) / (sqrtTwoPi * standardDeviation); +} + void main(void) { vec3 positionEC = pixelToEye(gl_FragCoord.xy); @@ -51,22 +58,25 @@ void main(void) } vec3 normalEC = getNormalXEdge(positionEC); + float samplingRadius = lengthCap * sqrt(-positionEC.z); - float ao = 0.0; - - const int ANGLE_STEPS = 4; + const int ANGLE_STEPS = 16; float angleStepScale = 1.0 / float(ANGLE_STEPS); float angleStep = angleStepScale * czm_twoPi; float cosStep = cos(angleStep); float sinStep = sin(angleStep); mat2 rotateStep = mat2(cosStep, sinStep, -sinStep, cosStep); + const int RADIAL_STEPS = 64; + float radialStepScale = 1.0 / float(RADIAL_STEPS); + // Initial sampling direction (different for each pixel) const float randomTextureSize = 255.0; vec2 randomTexCoord = fract(gl_FragCoord.xy / randomTextureSize); float randomVal = texture(randomTexture, randomTexCoord).x; vec2 sampleDirection = vec2(cos(angleStep * randomVal), sin(angleStep * randomVal)); + float ao = 0.0; // Loop over sampling directions for (int i = 0; i < ANGLE_STEPS; i++) { @@ -75,7 +85,7 @@ void main(void) float localAO = 0.0; vec2 radialStep = stepSize * sampleDirection; - for (int j = 0; j < 6; j++) + for (int j = 0; j < RADIAL_STEPS; j++) { // Step along sampling direction, away from output pixel vec2 newCoords = floor(gl_FragCoord.xy + float(j + 1) * radialStep) + vec2(0.5); @@ -90,25 +100,19 @@ void main(void) vec3 stepVector = stepPositionEC - positionEC; float stepLength = length(stepVector); - if (stepLength > lengthCap) - { - break; - } - float dotVal = clamp(dot(normalEC, normalize(stepVector)), 0.0, 1.0); if (dotVal < bias) { dotVal = 0.0; } - float weight = stepLength / lengthCap; - weight = 1.0 - weight * weight; - localAO = max(localAO, dotVal * weight); + float weight = gaussian(stepLength, samplingRadius); + localAO += weight * dotVal; } ao += localAO; } - ao *= angleStepScale; + ao *= angleStepScale * radialStepScale * sqrt(-positionEC.z); ao = 1.0 - clamp(ao, 0.0, 1.0); ao = pow(ao, intensity); out_FragColor = vec4(vec3(ao), 1.0); From aa2c569b8d62ce7f302af941518a63e4c613b493 Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Sat, 5 Oct 2024 16:59:36 +0200 Subject: [PATCH 002/175] First draft of GPM sandcastle --- .../development/NGA GPM Visualization.html | 1378 +++++++++++++++++ .../development/NGA GPM Visualization.jpg | Bin 0 -> 8429 bytes 2 files changed, 1378 insertions(+) create mode 100644 Apps/Sandcastle/gallery/development/NGA GPM Visualization.html create mode 100644 Apps/Sandcastle/gallery/development/NGA GPM Visualization.jpg diff --git a/Apps/Sandcastle/gallery/development/NGA GPM Visualization.html b/Apps/Sandcastle/gallery/development/NGA GPM Visualization.html new file mode 100644 index 000000000000..86f84132ba5c --- /dev/null +++ b/Apps/Sandcastle/gallery/development/NGA GPM Visualization.html @@ -0,0 +1,1378 @@ + + + + + + + + + Cesium Demo + + + + + + + +
+
+

Loading...

+
+
+ + + + + + + + + + + + + + + + + + + + + + + +
+

Cesium GPM Visualization

+
Data set + + + +
Anchor point ellipsoid + + + +
Shader mode + +
+
+ + + diff --git a/Apps/Sandcastle/gallery/development/NGA GPM Visualization.jpg b/Apps/Sandcastle/gallery/development/NGA GPM Visualization.jpg new file mode 100644 index 0000000000000000000000000000000000000000..5b2daea52ce03c25b298593a0d15c500b66cbe60 GIT binary patch literal 8429 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 g*D-aQhD6-}7cC(sse* Date: Sun, 6 Oct 2024 16:29:30 +0200 Subject: [PATCH 003/175] Proper handling of offset and scale in metadata picking --- .../engine/Source/Scene/DerivedCommand.js | 77 ++++++++-- .../engine/Source/Scene/MetadataPicking.js | 5 +- packages/engine/Specs/Scene/SceneSpec.js | 140 ++++++++++++++++-- 3 files changed, 197 insertions(+), 25 deletions(-) diff --git a/packages/engine/Source/Scene/DerivedCommand.js b/packages/engine/Source/Scene/DerivedCommand.js index 8d70e283ac6a..3baedea969c8 100644 --- a/packages/engine/Source/Scene/DerivedCommand.js +++ b/packages/engine/Source/Scene/DerivedCommand.js @@ -1,3 +1,4 @@ +import { MetadataComponentType } from "@cesium/engine"; import defined from "../Core/defined.js"; import DrawCommand from "../Renderer/DrawCommand.js"; import RenderState from "../Renderer/RenderState.js"; @@ -416,6 +417,55 @@ function getGlslType(classProperty) { return `ivec${componentCount}`; } +function unapplyValueTransform(input, offset, scale) { + return `((${input} - float(${offset})) / float(${scale}))`; +} +function unnormalize(input, componentType) { + const max = MetadataComponentType.getMaximum(componentType); + return `(${input}) / float(${max})`; +} + +function getSourceValueStringScalar(classProperty) { + let result = `float(value)`; + + // The 'hasValueTransform' indicates whether the class property + // did define an 'offset' or 'scale'. Even when they had not + // been defined, they receive default values in the constructor + // of MetadataClassProperty + if (classProperty.hasValueTransform) { + const offset = classProperty.offset; + const scale = classProperty.scale; + result = unapplyValueTransform(result, offset, scale); + } + if (!classProperty.normalized) { + result = unnormalize(result, classProperty.componentType); + } + return result; +} + +function getSourceValueStringComponent( + classProperty, + componentIndex, + componentName, +) { + const valueString = `value.${componentName}`; + let result = `float(${valueString})`; + + // The 'hasValueTransform' indicates whether the class property + // did define an 'offset' or 'scale'. Even when they had not + // been defined, they receive default values in the constructor + // of MetadataClassProperty + if (classProperty.hasValueTransform) { + const offset = classProperty.offset[componentIndex]; + const scale = classProperty.scale[componentIndex]; + result = unapplyValueTransform(result, offset, scale); + } + if (!classProperty.normalized) { + result = unnormalize(result, classProperty.componentType); + } + return result; +} + /** * Creates a new `ShaderProgram` from the given input that renders metadata * values into the frame buffer, according to the given picked metadata info. @@ -460,28 +510,27 @@ function getPickMetadataShaderProgram( const sourceValueStrings = ["0.0", "0.0", "0.0", "0.0"]; const componentCount = getComponentCount(classProperty); if (componentCount === 1) { - // When the property is a scalar, store its value directly - // in `metadataValues.x` - sourceValueStrings[0] = `float(value)`; + // When the property is a scalar, store the source value + // string directly in `metadataValues.x` + sourceValueStrings[0] = getSourceValueStringScalar(classProperty); } else { // When the property is an array, store the array elements // in `metadataValues.x/y/z/w` - const components = ["x", "y", "z", "w"]; + const componentNames = ["x", "y", "z", "w"]; for (let i = 0; i < componentCount; i++) { - const component = components[i]; - const valueString = `value.${component}`; - sourceValueStrings[i] = `float(${valueString})`; + sourceValueStrings[i] = getSourceValueStringComponent( + classProperty, + i, + componentNames[i], + ); } } - // Make sure that the `metadataValues` components are all in - // the range [0, 1] (which will result in RGBA components - // in [0, 255] during rendering) - if (!classProperty.normalized) { - for (let i = 0; i < componentCount; i++) { - sourceValueStrings[i] += " / 255.0"; - } + // XXX_DEBUG + for (let i = 0; i < componentCount; i++) { + console.log(`At ${i} source value string is "${sourceValueStrings[i]}"`); } + // XXX_DEBUG const newDefines = shaderProgram.fragmentShaderSource.defines.slice(); newDefines.push(MetadataPickingPipelineStage.METADATA_PICKING_ENABLED); diff --git a/packages/engine/Source/Scene/MetadataPicking.js b/packages/engine/Source/Scene/MetadataPicking.js index 87f33a91d7d3..036838709bba 100644 --- a/packages/engine/Source/Scene/MetadataPicking.js +++ b/packages/engine/Source/Scene/MetadataPicking.js @@ -295,11 +295,12 @@ MetadataPicking.decodeMetadataValues = function ( } return result; } - const result = MetadataPicking.convertToObjectType( + const basicResult = MetadataPicking.convertToObjectType( classProperty.type, arrayBasedResult, ); - return result; + const transformedResult = classProperty.applyValueTransform(basicResult); + return transformedResult; }; export default Object.freeze(MetadataPicking); diff --git a/packages/engine/Specs/Scene/SceneSpec.js b/packages/engine/Specs/Scene/SceneSpec.js index a2f21c789697..64b8c51f1da1 100644 --- a/packages/engine/Specs/Scene/SceneSpec.js +++ b/packages/engine/Specs/Scene/SceneSpec.js @@ -271,26 +271,52 @@ function createPropertyTextureGltfScalar() { type: "SCALAR", componentType: "UINT8", }, + }, + }, + }, + }; + const properties = { + example_UINT8_SCALAR: { + index: 0, + texCoord: 0, + channels: [0], + }, + }; + return createPropertyTextureGltf(schema, properties); +} + +/** + * Creates the glTF for the normalized 'scalar' test case + * + * @returns The glTF + */ +function createPropertyTextureGltfNormalizedScalar( + classPropertyOffset, + classPropertyScale, +) { + const schema = { + id: "ExampleSchema", + classes: { + exampleClass: { + name: "Example class", + properties: { example_normalized_UINT8_SCALAR: { name: "Example SCALAR property with normalized UINT8 components", type: "SCALAR", componentType: "UINT8", normalized: true, + offset: classPropertyOffset, + scale: classPropertyScale, }, }, }, }, }; const properties = { - example_UINT8_SCALAR: { - index: 0, - texCoord: 0, - channels: [0], - }, example_normalized_UINT8_SCALAR: { index: 0, texCoord: 0, - channels: [1], + channels: [0], }, }; return createPropertyTextureGltf(schema, properties); @@ -3114,7 +3140,12 @@ describe( const schemaId = undefined; const className = "exampleClass"; const propertyName = "example_normalized_UINT8_SCALAR"; - const gltf = createPropertyTextureGltfScalar(); + const classPropertyOffset = undefined; + const classPropertyScale = undefined; + const gltf = createPropertyTextureGltfNormalizedScalar( + classPropertyOffset, + classPropertyScale, + ); const canvasSizeX = textureSizeX * canvasScaling; const canvasSizeY = textureSizeY * canvasScaling; @@ -3144,16 +3175,16 @@ describe( schemaId, className, propertyName, - 3, 0, + 1, ); const actualMetadataValue2 = pickMetadataAt( scene, schemaId, className, propertyName, - 6, 0, + 2, ); const expectedMetadataValue0 = 0.0; const expectedMetadataValue1 = 0.5; @@ -3174,6 +3205,97 @@ describe( scene.destroyForSpecs(); }); + it("picks normalized UINT8 SCALAR with offset and scale from a property texture", async function () { + if (webglStub) { + return; + } + const schemaId = undefined; + const className = "exampleClass"; + const propertyName = "example_normalized_UINT8_SCALAR"; + const classPropertyOffset = 100.0; + const classPropertyScale = 2.0; + const gltf = createPropertyTextureGltfNormalizedScalar( + classPropertyOffset, + classPropertyScale, + ); + + const canvasSizeX = textureSizeX * canvasScaling; + const canvasSizeY = textureSizeY * canvasScaling; + const scene = createScene({ + canvas: createCanvas(canvasSizeX, canvasSizeY), + contextOptions: { + requestWebgl1: true, + }, + }); + + await loadAsModel(scene, gltf); + fitCameraToUnitSquare(scene.camera); + + scene.initializeFrame(); + scene.render(defaultDate); + + // XXX_DEBUG + for (let y = 0; y < 9; y++) { + for (let x = 0; x < 9; x++) { + const v = pickMetadataAt( + scene, + schemaId, + className, + propertyName, + x, + y, + ); + console.log(`At ${x} ${y} have ${v}`); + } + } + // XXX_DEBUG + + const actualMetadataValue0 = pickMetadataAt( + scene, + schemaId, + className, + propertyName, + 0, + 0, + ); + const actualMetadataValue1 = pickMetadataAt( + scene, + schemaId, + className, + propertyName, + 0, + 1, + ); + const actualMetadataValue2 = pickMetadataAt( + scene, + schemaId, + className, + propertyName, + 0, + 2, + ); + const expectedMetadataValue0 = + classPropertyOffset + classPropertyScale * 0.0; + const expectedMetadataValue1 = + classPropertyOffset + classPropertyScale * 0.5; + const expectedMetadataValue2 = + classPropertyOffset + classPropertyScale * 1.0; + + expect(actualMetadataValue0).toEqualEpsilon( + expectedMetadataValue0, + propertyValueEpsilon, + ); + expect(actualMetadataValue1).toEqualEpsilon( + expectedMetadataValue1, + propertyValueEpsilon, + ); + expect(actualMetadataValue2).toEqualEpsilon( + expectedMetadataValue2, + propertyValueEpsilon, + ); + scene.destroyForSpecs(); + }); + it("picks fixed length UINT8 SCALAR array from a property texture", async function () { if (webglStub) { return; From 97ae9db0d0ddd20ae9465c52ecb3c33cbd91e5de Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Tue, 8 Oct 2024 20:44:47 +0200 Subject: [PATCH 004/175] Towards handling offset/scale overrides - WIP --- .../engine/Source/Scene/DerivedCommand.js | 22 +++++ .../engine/Source/Scene/MetadataPicking.js | 75 ++++++++++++-- .../engine/Source/Scene/PickedMetadataInfo.js | 24 ++++- packages/engine/Source/Scene/Picking.js | 1 + packages/engine/Source/Scene/Scene.js | 20 +++- .../Source/Scene/getMetadataProperty.js | 56 +++++++++++ packages/engine/Specs/Scene/SceneSpec.js | 97 ++++++++++++++++++- 7 files changed, 282 insertions(+), 13 deletions(-) create mode 100644 packages/engine/Source/Scene/getMetadataProperty.js diff --git a/packages/engine/Source/Scene/DerivedCommand.js b/packages/engine/Source/Scene/DerivedCommand.js index 3baedea969c8..1c1045eed7ec 100644 --- a/packages/engine/Source/Scene/DerivedCommand.js +++ b/packages/engine/Source/Scene/DerivedCommand.js @@ -466,6 +466,26 @@ function getSourceValueStringComponent( return result; } +function debugPrintOverriddenOffsetScale(shaderProgram, propertyName) { + // Check for the presence of the value transform uniforms that may have + // been inserted by MetadataPipelineStage.addValueTransformUniforms. + // These MAY be different from the offset/scale of the class property, + // when they have been overridden by the property texture property. + const allUniforms = shaderProgram.allUniforms; + const offsetUniformName = `u_${propertyName}_offset`; + const scaleUniformName = `u_${propertyName}_scale`; + const offsetUniform = allUniforms[offsetUniformName]; + const scaleUniform = allUniforms[scaleUniformName]; + if (defined(offsetUniform) && defined(scaleUniform)) { + const offset = offsetUniform.value; + const scale = scaleUniform.value; + console.log("Now there they are, ", offset, scale); + } else { + console.log("Nope, not there"); + } + //return `czm_valueTransform(${offsetUniformName}, ${scaleUniformName}, ${valueExpression})`; +} + /** * Creates a new `ShaderProgram` from the given input that renders metadata * values into the frame buffer, according to the given picked metadata info. @@ -502,6 +522,8 @@ function getPickMetadataShaderProgram( return shader; } + debugPrintOverriddenOffsetScale(shaderProgram, propertyName); + const classProperty = pickedMetadataInfo.classProperty; const glslType = getGlslType(classProperty); diff --git a/packages/engine/Source/Scene/MetadataPicking.js b/packages/engine/Source/Scene/MetadataPicking.js index 036838709bba..df168d5cf33b 100644 --- a/packages/engine/Source/Scene/MetadataPicking.js +++ b/packages/engine/Source/Scene/MetadataPicking.js @@ -6,6 +6,7 @@ import Matrix2 from "../Core/Matrix2.js"; import Matrix3 from "../Core/Matrix3.js"; import Matrix4 from "../Core/Matrix4.js"; import RuntimeError from "../Core/RuntimeError.js"; +import MetadataClassProperty from "./MetadataClassProperty.js"; import MetadataComponentType from "./MetadataComponentType.js"; import MetadataType from "./MetadataType.js"; @@ -250,7 +251,7 @@ MetadataPicking.convertToObjectType = function (type, value) { case MetadataType.VEC3: return Cartesian3.unpack(numbers, 0, new Cartesian3()); case MetadataType.VEC4: - return Cartesian4.unpack(numbers, 0, new Cartesian3()); + return Cartesian4.unpack(numbers, 0, new Cartesian4()); case MetadataType.MAT2: return Matrix2.unpack(numbers, 0, new Matrix2()); case MetadataType.MAT3: @@ -262,11 +263,53 @@ MetadataPicking.convertToObjectType = function (type, value) { return value; }; +/** + * Converts the given type into an object representation where appropriate. + * + * For `VECn/MATn` types, the given value is converted into an array. + * For other types, an array containing the given value is + * returned. + * + * @param {string} type The `ClassProperty` type + * @param {any} value The input value + * @returns {any} The array representation + */ +MetadataPicking.convertToArray = function (type, value) { + if (!defined(value)) { + return value; + } + if ( + type === MetadataType.SCALAR || + type === MetadataType.STRING || + type === MetadataType.BOOLEAN || + type === MetadataType.ENUM + ) { + return [value]; + } + switch (type) { + case MetadataType.VEC2: + return Cartesian2.pack(value, Array(2)); + case MetadataType.VEC3: + return Cartesian3.pack(value, Array(3)); + case MetadataType.VEC4: + return Cartesian4.pack(value, Array(4)); + case MetadataType.MAT2: + return Matrix2.unpack(value, Array(4)); + case MetadataType.MAT3: + return Matrix3.unpack(value, Array(9)); + case MetadataType.MAT4: + return Matrix4.unpack(value, Array(16)); + } + // Should never happen: + return value; +}; + /** * Decode the given raw values into a metadata property value. * - * This just converts the result of `decodeRawMetadataValues` - * from array-based types into object types like `CartesianN`. + * This applies the value transform (offset/scale) to the result + * of `decodeRawMetadataValues`, and converts this from array-based + * types into object types like `CartesianN`. * * @param {MetadataClassProperty} classProperty The `MetadataClassProperty` * @param {Uint8Array} rawPixelValues The raw values @@ -276,12 +319,31 @@ MetadataPicking.convertToObjectType = function (type, value) { */ MetadataPicking.decodeMetadataValues = function ( classProperty, + metadataProperty, rawPixelValues, ) { - const arrayBasedResult = MetadataPicking.decodeRawMetadataValues( + let arrayBasedResult = MetadataPicking.decodeRawMetadataValues( classProperty, rawPixelValues, ); + + if (metadataProperty.hasValueTransform) { + const offset = MetadataPicking.convertToArray( + classProperty.type, + metadataProperty.offset, + ); + const scale = MetadataPicking.convertToArray( + classProperty.type, + metadataProperty.scale, + ); + arrayBasedResult = MetadataClassProperty.valueTransformInPlace( + arrayBasedResult, + offset, + scale, + MetadataComponentType.applyValueTransform, + ); + } + if (classProperty.isArray) { const arrayLength = classProperty.arrayLength; const result = Array(arrayLength); @@ -295,12 +357,11 @@ MetadataPicking.decodeMetadataValues = function ( } return result; } - const basicResult = MetadataPicking.convertToObjectType( + const objectResult = MetadataPicking.convertToObjectType( classProperty.type, arrayBasedResult, ); - const transformedResult = classProperty.applyValueTransform(basicResult); - return transformedResult; + return objectResult; }; export default Object.freeze(MetadataPicking); diff --git a/packages/engine/Source/Scene/PickedMetadataInfo.js b/packages/engine/Source/Scene/PickedMetadataInfo.js index 1e8de1902b17..3107b22e4d0a 100644 --- a/packages/engine/Source/Scene/PickedMetadataInfo.js +++ b/packages/engine/Source/Scene/PickedMetadataInfo.js @@ -6,11 +6,17 @@ * the metadata values of an object into the picking frame buffer. The * raw values are read from that buffer, and are then translated back into * proper metadata values in `Picking.pickMetadata`, using the structural - * information about the metadata `classProperty` that is stored here. + * information about the metadata that is stored here. * * @private */ -function PickedMetadataInfo(schemaId, className, propertyName, classProperty) { +function PickedMetadataInfo( + schemaId, + className, + propertyName, + classProperty, + metadataProperty, +) { /** * The optional ID of the metadata schema * @@ -29,11 +35,23 @@ function PickedMetadataInfo(schemaId, className, propertyName, classProperty) { * @type {string} */ this.propertyName = propertyName; + /** - * The optional ID of the metadata schema + * The the `MetadataClassProperty` that is described by this + * structure, as obtained from the `MetadataSchema` * * @type {MetadataClassProperty} */ this.classProperty = classProperty; + + /** + * The the `PropertyTextureProperty` or `PropertyAttributeProperty` + * that is described by this structure, as obtained from the + * property texture or property attribute of the `StructuralMetadata` + * that matches the class name and property name. + * + * @type {PropertyTextureProperty|PropertyAttributeProperty} + */ + this.metadataProperty = metadataProperty; } export default PickedMetadataInfo; diff --git a/packages/engine/Source/Scene/Picking.js b/packages/engine/Source/Scene/Picking.js index fc38a4a90955..8ded3ccbe400 100644 --- a/packages/engine/Source/Scene/Picking.js +++ b/packages/engine/Source/Scene/Picking.js @@ -517,6 +517,7 @@ Picking.prototype.pickMetadata = function ( const metadataValue = MetadataPicking.decodeMetadataValues( pickedMetadataInfo.classProperty, + pickedMetadataInfo.metadataProperty, rawMetadataPixel, ); diff --git a/packages/engine/Source/Scene/Scene.js b/packages/engine/Source/Scene/Scene.js index e751dda94087..3cf1b4b9bcec 100644 --- a/packages/engine/Source/Scene/Scene.js +++ b/packages/engine/Source/Scene/Scene.js @@ -79,6 +79,7 @@ import VoxelCell from "./VoxelCell.js"; import VoxelPrimitive from "./VoxelPrimitive.js"; import getMetadataClassProperty from "./getMetadataClassProperty.js"; import PickedMetadataInfo from "./PickedMetadataInfo.js"; +import getMetadataProperty from "./getMetadataProperty.js"; const requestRenderAfterFrame = function (scene) { return function () { @@ -4426,7 +4427,11 @@ Scene.prototype.pickMetadata = function ( // Check if the picked object is a model that has structural // metadata, with a schema that contains the specified // property. - const schema = pickedObject.detail?.model?.structuralMetadata?.schema; + const structuralMetadata = pickedObject.detail?.model?.structuralMetadata; + if (!defined(structuralMetadata)) { + return undefined; + } + const schema = structuralMetadata.schema; const classProperty = getMetadataClassProperty( schema, schemaId, @@ -4436,12 +4441,25 @@ Scene.prototype.pickMetadata = function ( if (!defined(classProperty)) { return undefined; } + const metadataProperty = getMetadataProperty( + structuralMetadata, + className, + propertyName, + ); + if (!defined(metadataProperty)) { + return undefined; + } + + // XXX DEBUG + console.log("Using metadata property ", metadataProperty); + // XXX DEBUG const pickedMetadataInfo = new PickedMetadataInfo( schemaId, className, propertyName, classProperty, + metadataProperty, ); const pickedMetadataValues = this._picking.pickMetadata( diff --git a/packages/engine/Source/Scene/getMetadataProperty.js b/packages/engine/Source/Scene/getMetadataProperty.js new file mode 100644 index 000000000000..1d5e8ea246f0 --- /dev/null +++ b/packages/engine/Source/Scene/getMetadataProperty.js @@ -0,0 +1,56 @@ +import defined from "../Core/defined.js"; + +/** + * Return the `PropertyTextureProperty` or `PropertyAttributeProperty` from + * the given `StructuralMetadata` that matches the given description. + * + * If the given structural metadata is `undefined`, then `undefined` is returned. + * + * Otherwise, this method will check all the property textures and property + * attributes in the given structural metadata. + * + * If it finds a property texture that has a class with an `_id` that matches + * the given name, and that contains a property for the given property name, then + * this property is returned. + * + * If it finds a property attribute that has a class with an `_id` that matches + * the given name, and that contains a property for the given property name, then + * this property is returned. + * + * Otherwise, `undefined` is returned + * + * @param {StructuralMetadata} structuralMetadata The structural metadata + * @param {string} className The name of the metadata class + * @param {string} propertyName The name of the metadata property + * @returns {PropertyTextureProperty|PropertyAttributeProperty|undefined} + * @private + */ +function getMetadataProperty(structuralMetadata, className, propertyName) { + if (!defined(structuralMetadata)) { + return undefined; + } + const propertyTextures = structuralMetadata.propertyTextures; + for (const propertyTexture of propertyTextures) { + const metadataClass = propertyTexture.class; + if (metadataClass.id === className) { + const properties = propertyTexture.properties; + const property = properties[propertyName]; + if (defined(property)) { + return property; + } + } + } + const propertyAttributes = structuralMetadata.propertyAttributes; + for (const propertyAttribute of propertyAttributes) { + const metadataClass = propertyAttribute.class; + if (metadataClass.id === className) { + const properties = propertyAttribute.properties; + const property = properties[propertyName]; + if (defined(property)) { + return property; + } + } + } +} + +export default getMetadataProperty; diff --git a/packages/engine/Specs/Scene/SceneSpec.js b/packages/engine/Specs/Scene/SceneSpec.js index 64b8c51f1da1..35c7d537fd98 100644 --- a/packages/engine/Specs/Scene/SceneSpec.js +++ b/packages/engine/Specs/Scene/SceneSpec.js @@ -391,7 +391,10 @@ function createPropertyTextureGltfVec2() { * * @returns The glTF */ -function createPropertyTextureGltfNormalizedVec2() { +function createPropertyTextureGltfNormalizedVec2( + classPropertyOffset, + classPropertyScale, +) { const schema = { id: "ExampleSchema", classes: { @@ -403,6 +406,8 @@ function createPropertyTextureGltfNormalizedVec2() { type: "VEC2", componentType: "UINT8", normalized: true, + offset: classPropertyOffset, + scale: classPropertyScale, }, }, }, @@ -3439,7 +3444,12 @@ describe( const schemaId = undefined; const className = "exampleClass"; const propertyName = "example_normalized_UINT8_VEC2"; - const gltf = createPropertyTextureGltfNormalizedVec2(); + const classPropertyOffset = undefined; + const classPropertyScale = undefined; + const gltf = createPropertyTextureGltfNormalizedVec2( + classPropertyOffset, + classPropertyScale, + ); const canvasSizeX = textureSizeX * canvasScaling; const canvasSizeY = textureSizeY * canvasScaling; @@ -3500,6 +3510,89 @@ describe( scene.destroyForSpecs(); }); + it("picks normalized UINT8 VEC2 with offset and scale from a property texture", async function () { + if (webglStub) { + return; + } + + const schemaId = undefined; + const className = "exampleClass"; + const propertyName = "example_normalized_UINT8_VEC2"; + const classPropertyOffset = [100, 200]; + const classPropertyScale = [2, 3]; + const gltf = createPropertyTextureGltfNormalizedVec2( + classPropertyOffset, + classPropertyScale, + ); + + const canvasSizeX = textureSizeX * canvasScaling; + const canvasSizeY = textureSizeY * canvasScaling; + const scene = createScene({ + canvas: createCanvas(canvasSizeX, canvasSizeY), + contextOptions: { + requestWebgl1: true, + }, + }); + + await loadAsModel(scene, gltf); + fitCameraToUnitSquare(scene.camera); + + scene.initializeFrame(); + scene.render(defaultDate); + + const actualMetadataValue0 = pickMetadataAt( + scene, + schemaId, + className, + propertyName, + 0, + 0, + ); + const actualMetadataValue1 = pickMetadataAt( + scene, + schemaId, + className, + propertyName, + 1, + 1, + ); + const actualMetadataValue2 = pickMetadataAt( + scene, + schemaId, + className, + propertyName, + 2, + 2, + ); + + const expectedMetadataValue0 = new Cartesian2( + classPropertyOffset[0] + classPropertyScale[0] * 0.0, + classPropertyOffset[1] + classPropertyScale[1] * 0.0, + ); + const expectedMetadataValue1 = new Cartesian2( + classPropertyOffset[0] + classPropertyScale[0] * 0.5, + classPropertyOffset[1] + classPropertyScale[1] * 0.0, + ); + const expectedMetadataValue2 = new Cartesian2( + classPropertyOffset[0] + classPropertyScale[0] * 1.0, + classPropertyOffset[1] + classPropertyScale[1] * 0.0, + ); + + expect(actualMetadataValue0).toEqualEpsilon( + expectedMetadataValue0, + propertyValueEpsilon, + ); + expect(actualMetadataValue1).toEqualEpsilon( + expectedMetadataValue1, + propertyValueEpsilon, + ); + expect(actualMetadataValue2).toEqualEpsilon( + expectedMetadataValue2, + propertyValueEpsilon, + ); + scene.destroyForSpecs(); + }); + it("picks UINT8 VEC3 from a property texture", async function () { if (webglStub) { return; From 13eaedd9e3abab6b461a49638113a3bd4cbb6a03 Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Wed, 9 Oct 2024 16:49:19 +0200 Subject: [PATCH 005/175] Additional comments and JSDoc --- .../engine/Source/Scene/DerivedCommand.js | 3 +++ .../Source/Scene/MetadataClassProperty.js | 23 +++++++++++++++++++ .../Source/Scene/PropertyAttributeProperty.js | 18 ++++++++++++--- .../Source/Scene/PropertyTextureProperty.js | 12 ++++++++++ 4 files changed, 53 insertions(+), 3 deletions(-) diff --git a/packages/engine/Source/Scene/DerivedCommand.js b/packages/engine/Source/Scene/DerivedCommand.js index 1c1045eed7ec..1e05629346e3 100644 --- a/packages/engine/Source/Scene/DerivedCommand.js +++ b/packages/engine/Source/Scene/DerivedCommand.js @@ -528,6 +528,9 @@ function getPickMetadataShaderProgram( const glslType = getGlslType(classProperty); // Define the components that will go into the output `metadataValues`. + // This will be the 'color' that is written into the frame buffer, + // meaning that the values should be in [0.0, 1.0], and will become + // values in [0, 255] in the frame buffer. // By default, all of them are 0.0. const sourceValueStrings = ["0.0", "0.0", "0.0", "0.0"]; const componentCount = getComponentCount(classProperty); diff --git a/packages/engine/Source/Scene/MetadataClassProperty.js b/packages/engine/Source/Scene/MetadataClassProperty.js index dec9b1393e3e..5683d5c8501e 100644 --- a/packages/engine/Source/Scene/MetadataClassProperty.js +++ b/packages/engine/Source/Scene/MetadataClassProperty.js @@ -438,6 +438,10 @@ Object.defineProperties(MetadataClassProperty.prototype, { /** * The offset to be added to property values as part of the value transform. * + * This is always defined, even when `hasValueTransform` is `false`. If + * the class property JSON itself did not define it, then it will be + * initialized to the default value. + * * @memberof MetadataClassProperty.prototype * @type {number|number[]|number[][]} * @readonly @@ -451,6 +455,10 @@ Object.defineProperties(MetadataClassProperty.prototype, { /** * The scale to be multiplied to property values as part of the value transform. * + * This is always defined, even when `hasValueTransform` is `false`. If + * the class property JSON itself did not define it, then it will be + * initialized to the default value. + * * @memberof MetadataClassProperty.prototype * @type {number|number[]|number[][]} * @readonly @@ -1139,6 +1147,21 @@ function normalizeInPlace(values, valueType, normalizeFunction) { } /** + * Applies the value transform that is defined with the given offsets + * and scales to the given values. + * + * If the given values are not an array, then the given transformation + * function will be applied directly. + * + * If the values are an array, then this function will be called recursively + * with the array elements, boiling down to a component-wise application + * of the transformation function to the innermost array elements. + * + * @param {number|number[]|number[][]} values The input values + * @param {number|number[]|number[][]} offsets The offsets + * @param {number|number[]|number[][]} scales The scales + * @returns The input values (or the result of applying the transformation + * function to a single value if the values have not been an array). * @private */ MetadataClassProperty.valueTransformInPlace = function ( diff --git a/packages/engine/Source/Scene/PropertyAttributeProperty.js b/packages/engine/Source/Scene/PropertyAttributeProperty.js index 607fb8611470..70c04cfd1ab4 100644 --- a/packages/engine/Source/Scene/PropertyAttributeProperty.js +++ b/packages/engine/Source/Scene/PropertyAttributeProperty.js @@ -80,7 +80,7 @@ Object.defineProperties(PropertyAttributeProperty.prototype, { * True if offset/scale should be applied. If both offset/scale were * undefined, they default to identity so this property is set false * - * @memberof MetadataClassProperty.prototype + * @memberof PropertyAttributeProperty.prototype * @type {boolean} * @readonly * @private @@ -94,7 +94,13 @@ Object.defineProperties(PropertyAttributeProperty.prototype, { /** * The offset to be added to property values as part of the value transform. * - * @memberof MetadataClassProperty.prototype + * This is always defined, even when `hasValueTransform` is `false`. If + * the property JSON itself did not define it, then it will inherit the + * value from the `MetadataClassProperty`. There, it also is always + * defined, and initialized to the default value if it was not contained + * in the class property JSON. + * + * @memberof PropertyAttributeProperty.prototype * @type {number|Cartesian2|Cartesian3|Cartesian4|Matrix2|Matrix3|Matrix4} * @readonly * @private @@ -108,7 +114,13 @@ Object.defineProperties(PropertyAttributeProperty.prototype, { /** * The scale to be multiplied to property values as part of the value transform. * - * @memberof MetadataClassProperty.prototype + * This is always defined, even when `hasValueTransform` is `false`. If + * the property JSON itself did not define it, then it will inherit the + * value from the `MetadataClassProperty`. There, it also is always + * defined, and initialized to the default value if it was not contained + * in the class property JSON. + * + * @memberof PropertyAttributeProperty.prototype * @type {number|Cartesian2|Cartesian3|Cartesian4|Matrix2|Matrix3|Matrix4} * @readonly * @private diff --git a/packages/engine/Source/Scene/PropertyTextureProperty.js b/packages/engine/Source/Scene/PropertyTextureProperty.js index 765c2507313e..cbb7508cc11e 100644 --- a/packages/engine/Source/Scene/PropertyTextureProperty.js +++ b/packages/engine/Source/Scene/PropertyTextureProperty.js @@ -111,6 +111,12 @@ Object.defineProperties(PropertyTextureProperty.prototype, { /** * The offset to be added to property values as part of the value transform. * + * This is always defined, even when `hasValueTransform` is `false`. If + * the property JSON itself did not define it, then it will inherit the + * value from the `MetadataClassProperty`. There, it also is always + * defined, and initialized to the default value if it was not contained + * in the class property JSON. + * * @memberof PropertyTextureProperty.prototype * @type {number|Cartesian2|Cartesian3|Cartesian4|Matrix2|Matrix3|Matrix4} * @readonly @@ -125,6 +131,12 @@ Object.defineProperties(PropertyTextureProperty.prototype, { /** * The scale to be multiplied to property values as part of the value transform. * + * This is always defined, even when `hasValueTransform` is `false`. If + * the property JSON itself did not define it, then it will inherit the + * value from the `MetadataClassProperty`. There, it also is always + * defined, and initialized to the default value if it was not contained + * in the class property JSON. + * * @memberof PropertyTextureProperty.prototype * @type {number|Cartesian2|Cartesian3|Cartesian4|Matrix2|Matrix3|Matrix4} * @readonly From ed6ad00c538d19c26524f3478f48794591665d66 Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Wed, 9 Oct 2024 19:58:15 +0200 Subject: [PATCH 006/175] Comments and specs --- .../engine/Source/Scene/DerivedCommand.js | 103 ++-- .../engine/Source/Scene/MetadataPicking.js | 13 +- packages/engine/Specs/Scene/SceneSpec.js | 447 +++++++++++++++++- 3 files changed, 496 insertions(+), 67 deletions(-) diff --git a/packages/engine/Source/Scene/DerivedCommand.js b/packages/engine/Source/Scene/DerivedCommand.js index 1e05629346e3..76e1fcae669c 100644 --- a/packages/engine/Source/Scene/DerivedCommand.js +++ b/packages/engine/Source/Scene/DerivedCommand.js @@ -417,24 +417,51 @@ function getGlslType(classProperty) { return `ivec${componentCount}`; } +/** + * Returns a shader statement that applies the inverse of the + * value transform to the given value, based on the given offset + * and scale. + * + * @param {string} input The input value + * @param {string} offset The offset + * @param {string} scale The scale + * @returns {string} The statement + */ function unapplyValueTransform(input, offset, scale) { return `((${input} - float(${offset})) / float(${scale}))`; } + +/** + * Returns a shader statement that applies the inverse of the + * normalization, based on the given component type + * + * @param {string} input The input value + * @param {string} componentType The component type + * @returns {string} The statement + */ function unnormalize(input, componentType) { const max = MetadataComponentType.getMaximum(componentType); return `(${input}) / float(${max})`; } -function getSourceValueStringScalar(classProperty) { +/** + * Creates a shader statement that returns the value of the specified + * property, normalized to the range [0, 1]. + * + * @param {MetadataClassProperty} classProperty The class property + * @param {PropertyTextureProperty|PropertyAttributeProperty} metadataProperty The metadata property + * @returns The string + */ +function getSourceValueStringScalar(classProperty, metadataProperty) { let result = `float(value)`; - // The 'hasValueTransform' indicates whether the class property - // did define an 'offset' or 'scale'. Even when they had not - // been defined, they receive default values in the constructor - // of MetadataClassProperty - if (classProperty.hasValueTransform) { - const offset = classProperty.offset; - const scale = classProperty.scale; + // The 'hasValueTransform' indicates whether the property + // (or its class property) did define an 'offset' or 'scale'. + // Even when they had not been defined in the JSON, they are + // defined in the object, with default values. + if (metadataProperty.hasValueTransform) { + const offset = metadataProperty.offset; + const scale = metadataProperty.scale; result = unapplyValueTransform(result, offset, scale); } if (!classProperty.normalized) { @@ -443,21 +470,35 @@ function getSourceValueStringScalar(classProperty) { return result; } +/** + * Creates a shader statement that returns the value of the specified + * component of the given property, normalized to the range [0, 1]. + * + * @param {MetadataClassProperty} classProperty The class property + * @param {PropertyTextureProperty|PropertyAttributeProperty} metadataProperty The metadata property + * @param {string} componentName The name, in ["x", "y", "z", "w"] + * @returns The string + */ function getSourceValueStringComponent( classProperty, - componentIndex, + metadataProperty, componentName, ) { const valueString = `value.${componentName}`; let result = `float(${valueString})`; - // The 'hasValueTransform' indicates whether the class property - // did define an 'offset' or 'scale'. Even when they had not - // been defined, they receive default values in the constructor - // of MetadataClassProperty - if (classProperty.hasValueTransform) { - const offset = classProperty.offset[componentIndex]; - const scale = classProperty.scale[componentIndex]; + // The 'hasValueTransform' indicates whether the property + // (or its class property) did define an 'offset' or 'scale'. + // Even when they had not been defined in the JSON, they are + // defined in the object, with default values + // Note that in the 'PropertyTextureProperty' and the + // 'PropertyAttributeProperty', these values are + // stored as "object types" (like 'Cartesian2'), whereas + // in the 'MetadataClassProperty', they are stored as + // "array types", e.g. a `[number, number]` + if (metadataProperty.hasValueTransform) { + const offset = metadataProperty.offset[componentName]; + const scale = metadataProperty.scale[componentName]; result = unapplyValueTransform(result, offset, scale); } if (!classProperty.normalized) { @@ -466,26 +507,6 @@ function getSourceValueStringComponent( return result; } -function debugPrintOverriddenOffsetScale(shaderProgram, propertyName) { - // Check for the presence of the value transform uniforms that may have - // been inserted by MetadataPipelineStage.addValueTransformUniforms. - // These MAY be different from the offset/scale of the class property, - // when they have been overridden by the property texture property. - const allUniforms = shaderProgram.allUniforms; - const offsetUniformName = `u_${propertyName}_offset`; - const scaleUniformName = `u_${propertyName}_scale`; - const offsetUniform = allUniforms[offsetUniformName]; - const scaleUniform = allUniforms[scaleUniformName]; - if (defined(offsetUniform) && defined(scaleUniform)) { - const offset = offsetUniform.value; - const scale = scaleUniform.value; - console.log("Now there they are, ", offset, scale); - } else { - console.log("Nope, not there"); - } - //return `czm_valueTransform(${offsetUniformName}, ${scaleUniformName}, ${valueExpression})`; -} - /** * Creates a new `ShaderProgram` from the given input that renders metadata * values into the frame buffer, according to the given picked metadata info. @@ -522,8 +543,7 @@ function getPickMetadataShaderProgram( return shader; } - debugPrintOverriddenOffsetScale(shaderProgram, propertyName); - + const metadataProperty = pickedMetadataInfo.metadataProperty; const classProperty = pickedMetadataInfo.classProperty; const glslType = getGlslType(classProperty); @@ -537,7 +557,10 @@ function getPickMetadataShaderProgram( if (componentCount === 1) { // When the property is a scalar, store the source value // string directly in `metadataValues.x` - sourceValueStrings[0] = getSourceValueStringScalar(classProperty); + sourceValueStrings[0] = getSourceValueStringScalar( + classProperty, + metadataProperty, + ); } else { // When the property is an array, store the array elements // in `metadataValues.x/y/z/w` @@ -545,7 +568,7 @@ function getPickMetadataShaderProgram( for (let i = 0; i < componentCount; i++) { sourceValueStrings[i] = getSourceValueStringComponent( classProperty, - i, + metadataProperty, componentNames[i], ); } diff --git a/packages/engine/Source/Scene/MetadataPicking.js b/packages/engine/Source/Scene/MetadataPicking.js index df168d5cf33b..6972bd51f2d5 100644 --- a/packages/engine/Source/Scene/MetadataPicking.js +++ b/packages/engine/Source/Scene/MetadataPicking.js @@ -264,17 +264,16 @@ MetadataPicking.convertToObjectType = function (type, value) { }; /** - * Converts the given type into an object representation where appropriate. + * Converts the given type into an raw value or array representation. * * For `VECn/MATn` types, the given value is converted into an array. - * For other types, an array containing the given value is - * returned. + * For other types, the value is returned directly * * @param {string} type The `ClassProperty` type * @param {any} value The input value * @returns {any} The array representation */ -MetadataPicking.convertToArray = function (type, value) { +MetadataPicking.convertFromObjectType = function (type, value) { if (!defined(value)) { return value; } @@ -284,7 +283,7 @@ MetadataPicking.convertToArray = function (type, value) { type === MetadataType.BOOLEAN || type === MetadataType.ENUM ) { - return [value]; + return value; } switch (type) { case MetadataType.VEC2: @@ -328,11 +327,11 @@ MetadataPicking.decodeMetadataValues = function ( ); if (metadataProperty.hasValueTransform) { - const offset = MetadataPicking.convertToArray( + const offset = MetadataPicking.convertFromObjectType( classProperty.type, metadataProperty.offset, ); - const scale = MetadataPicking.convertToArray( + const scale = MetadataPicking.convertFromObjectType( classProperty.type, metadataProperty.scale, ); diff --git a/packages/engine/Specs/Scene/SceneSpec.js b/packages/engine/Specs/Scene/SceneSpec.js index 35c7d537fd98..68a2bd1bbb1c 100644 --- a/packages/engine/Specs/Scene/SceneSpec.js +++ b/packages/engine/Specs/Scene/SceneSpec.js @@ -288,11 +288,21 @@ function createPropertyTextureGltfScalar() { /** * Creates the glTF for the normalized 'scalar' test case * + * @param {number|undefined} classPropertyOffset The optional offset + * that will be defined in the class property definition + * @param {number|undefined} classPropertyScale The optional scale + * that will be defined in the class property definition + * @param {number|undefined} metadataPropertyOffset The optional offset + * that will be defined in the property texture property definition + * @param {number|undefined} metadataPropertyScale The optional scale + * that will be defined in the property texture property definition * @returns The glTF */ function createPropertyTextureGltfNormalizedScalar( classPropertyOffset, classPropertyScale, + metadataPropertyOffset, + metadataPropertyScale, ) { const schema = { id: "ExampleSchema", @@ -317,6 +327,8 @@ function createPropertyTextureGltfNormalizedScalar( index: 0, texCoord: 0, channels: [0], + offset: metadataPropertyOffset, + scale: metadataPropertyScale, }, }; return createPropertyTextureGltf(schema, properties); @@ -355,6 +367,57 @@ function createPropertyTextureGltfScalarArray() { return createPropertyTextureGltf(schema, properties); } +/** + * Creates the glTF for the 'normalized scalar array' test case + * + * @param {number[]|undefined} classPropertyOffset The optional offset + * that will be defined in the class property definition + * @param {number[]|undefined} classPropertyScale The optional scale + * that will be defined in the class property definition + * @param {number[]|undefined} metadataPropertyOffset The optional offset + * that will be defined in the property texture property definition + * @param {number[]|undefined} metadataPropertyScale The optional scale + * that will be defined in the property texture property definition + * @returns The glTF + */ +function createPropertyTextureGltfNormalizedScalarArray( + classPropertyOffset, + classPropertyScale, + metadataPropertyOffset, + metadataPropertyScale, +) { + const schema = { + id: "ExampleSchema", + classes: { + exampleClass: { + name: "Example class", + properties: { + example_fixed_length_normalized_UINT8_SCALAR_array: { + name: "Example fixed-length SCALAR array property with normalized INT8 components", + type: "SCALAR", + componentType: "UINT8", + array: true, + count: 3, + normalized: true, + offset: classPropertyOffset, + scale: classPropertyScale, + }, + }, + }, + }, + }; + const properties = { + example_fixed_length_normalized_UINT8_SCALAR_array: { + index: 0, + texCoord: 0, + channels: [0, 1, 2], + offset: metadataPropertyOffset, + scale: metadataPropertyScale, + }, + }; + return createPropertyTextureGltf(schema, properties); +} + /** * Creates the glTF for the 'vec2' test case * @@ -389,11 +452,21 @@ function createPropertyTextureGltfVec2() { /** * Creates the glTF for the normalized 'vec2' test case * + * @param {number[]|undefined} classPropertyOffset The optional offset + * that will be defined in the class property definition + * @param {number[]|undefined} classPropertyScale The optional scale + * that will be defined in the class property definition + * @param {number[]|undefined} metadataPropertyOffset The optional offset + * that will be defined in the property texture property definition + * @param {number[]|undefined} metadataPropertyScale The optional scale + * that will be defined in the property texture property definition * @returns The glTF */ function createPropertyTextureGltfNormalizedVec2( classPropertyOffset, classPropertyScale, + metadataPropertyOffset, + metadataPropertyScale, ) { const schema = { id: "ExampleSchema", @@ -418,6 +491,8 @@ function createPropertyTextureGltfNormalizedVec2( index: 0, texCoord: 0, channels: [0, 1], + offset: metadataPropertyOffset, + scale: metadataPropertyScale, }, }; return createPropertyTextureGltf(schema, properties); @@ -3147,9 +3222,13 @@ describe( const propertyName = "example_normalized_UINT8_SCALAR"; const classPropertyOffset = undefined; const classPropertyScale = undefined; + const metadataPropertyOffset = undefined; + const metadataPropertyScale = undefined; const gltf = createPropertyTextureGltfNormalizedScalar( classPropertyOffset, classPropertyScale, + metadataPropertyOffset, + metadataPropertyScale, ); const canvasSizeX = textureSizeX * canvasScaling; @@ -3210,7 +3289,7 @@ describe( scene.destroyForSpecs(); }); - it("picks normalized UINT8 SCALAR with offset and scale from a property texture", async function () { + it("picks normalized UINT8 SCALAR from a property texture with offset and scale in class property", async function () { if (webglStub) { return; } @@ -3219,9 +3298,13 @@ describe( const propertyName = "example_normalized_UINT8_SCALAR"; const classPropertyOffset = 100.0; const classPropertyScale = 2.0; + const metadataPropertyOffset = undefined; + const metadataPropertyScale = undefined; const gltf = createPropertyTextureGltfNormalizedScalar( classPropertyOffset, classPropertyScale, + metadataPropertyOffset, + metadataPropertyScale, ); const canvasSizeX = textureSizeX * canvasScaling; @@ -3239,22 +3322,6 @@ describe( scene.initializeFrame(); scene.render(defaultDate); - // XXX_DEBUG - for (let y = 0; y < 9; y++) { - for (let x = 0; x < 9; x++) { - const v = pickMetadataAt( - scene, - schemaId, - className, - propertyName, - x, - y, - ); - console.log(`At ${x} ${y} have ${v}`); - } - } - // XXX_DEBUG - const actualMetadataValue0 = pickMetadataAt( scene, schemaId, @@ -3301,6 +3368,86 @@ describe( scene.destroyForSpecs(); }); + it("picks normalized UINT8 SCALAR from a property texture with offset and scale in property texture property", async function () { + if (webglStub) { + return; + } + const schemaId = undefined; + const className = "exampleClass"; + const propertyName = "example_normalized_UINT8_SCALAR"; + const classPropertyOffset = 100.0; + const classPropertyScale = 200.0; + // These should override the values from the class property: + const metadataPropertyOffset = 200.0; + const metadataPropertyScale = 3.0; + const gltf = createPropertyTextureGltfNormalizedScalar( + classPropertyOffset, + classPropertyScale, + metadataPropertyOffset, + metadataPropertyScale, + ); + + const canvasSizeX = textureSizeX * canvasScaling; + const canvasSizeY = textureSizeY * canvasScaling; + const scene = createScene({ + canvas: createCanvas(canvasSizeX, canvasSizeY), + contextOptions: { + requestWebgl1: true, + }, + }); + + await loadAsModel(scene, gltf); + fitCameraToUnitSquare(scene.camera); + + scene.initializeFrame(); + scene.render(defaultDate); + + const actualMetadataValue0 = pickMetadataAt( + scene, + schemaId, + className, + propertyName, + 0, + 0, + ); + const actualMetadataValue1 = pickMetadataAt( + scene, + schemaId, + className, + propertyName, + 0, + 1, + ); + const actualMetadataValue2 = pickMetadataAt( + scene, + schemaId, + className, + propertyName, + 0, + 2, + ); + const expectedMetadataValue0 = + metadataPropertyOffset + metadataPropertyScale * 0.0; + const expectedMetadataValue1 = + metadataPropertyOffset + metadataPropertyScale * 0.5; + const expectedMetadataValue2 = + metadataPropertyOffset + metadataPropertyScale * 1.0; + + expect(actualMetadataValue0).toEqualEpsilon( + expectedMetadataValue0, + propertyValueEpsilon, + ); + expect(actualMetadataValue1).toEqualEpsilon( + expectedMetadataValue1, + propertyValueEpsilon, + ); + expect(actualMetadataValue2).toEqualEpsilon( + expectedMetadataValue2, + propertyValueEpsilon, + ); + scene.destroyForSpecs(); + }); + it("picks fixed length UINT8 SCALAR array from a property texture", async function () { if (webglStub) { return; @@ -3368,6 +3515,170 @@ describe( scene.destroyForSpecs(); }); + it("picks fixed length normalized UINT8 SCALAR array from a property texture", async function () { + if (webglStub) { + return; + } + const schemaId = undefined; + const className = "exampleClass"; + const propertyName = "example_fixed_length_normalized_UINT8_SCALAR_array"; + const classPropertyOffset = undefined; + const classPropertyScale = undefined; + const metadataPropertyOffset = undefined; + const metadataPropertyScale = undefined; + const gltf = createPropertyTextureGltfNormalizedScalarArray( + classPropertyOffset, + classPropertyScale, + metadataPropertyOffset, + metadataPropertyScale, + ); + + const canvasSizeX = textureSizeX * canvasScaling; + const canvasSizeY = textureSizeY * canvasScaling; + const scene = createScene({ + canvas: createCanvas(canvasSizeX, canvasSizeY), + contextOptions: { + requestWebgl1: true, + }, + }); + + await loadAsModel(scene, gltf); + fitCameraToUnitSquare(scene.camera); + + scene.initializeFrame(); + scene.render(defaultDate); + + const actualMetadataValue0 = pickMetadataAt( + scene, + schemaId, + className, + propertyName, + 0, + 0, + ); + const actualMetadataValue1 = pickMetadataAt( + scene, + schemaId, + className, + propertyName, + 1, + 1, + ); + const actualMetadataValue2 = pickMetadataAt( + scene, + schemaId, + className, + propertyName, + 2, + 2, + ); + const expectedMetadataValue0 = [0, 0, 0]; + const expectedMetadataValue1 = [0.5, 0, 0.5]; + const expectedMetadataValue2 = [1.0, 0, 1.0]; + + expect(actualMetadataValue0).toEqualEpsilon( + expectedMetadataValue0, + propertyValueEpsilon, + ); + expect(actualMetadataValue1).toEqualEpsilon( + expectedMetadataValue1, + propertyValueEpsilon, + ); + expect(actualMetadataValue2).toEqualEpsilon( + expectedMetadataValue2, + propertyValueEpsilon, + ); + scene.destroyForSpecs(); + }); + + // XXX TODO Shader compilation error that seems to be unrelated + // to metadata picking. Test this in isolation! + /* + it("picks fixed length normalized UINT8 SCALAR array from a property texture with offset and scale in class property", async function () { + if (webglStub) { + return; + } + const schemaId = undefined; + const className = "exampleClass"; + const propertyName = "example_fixed_length_normalized_UINT8_SCALAR_array"; + const classPropertyOffset = [100, 200, 300 ]; + const classPropertyScale = [2, 3, 4]; + const metadataPropertyOffset = undefined; + const metadataPropertyScale = undefined; + const gltf = createPropertyTextureGltfNormalizedScalarArray( + classPropertyOffset, + classPropertyScale, + metadataPropertyOffset, + metadataPropertyScale, + ); + + const canvasSizeX = textureSizeX * canvasScaling; + const canvasSizeY = textureSizeY * canvasScaling; + const scene = createScene({ + canvas: createCanvas(canvasSizeX, canvasSizeY), + contextOptions: { + requestWebgl1: true, + }, + }); + + await loadAsModel(scene, gltf); + fitCameraToUnitSquare(scene.camera); + + scene.initializeFrame(); + scene.render(defaultDate); + + const actualMetadataValue0 = pickMetadataAt( + scene, + schemaId, + className, + propertyName, + 0, + 0, + ); + const actualMetadataValue1 = pickMetadataAt( + scene, + schemaId, + className, + propertyName, + 1, + 1, + ); + const actualMetadataValue2 = pickMetadataAt( + scene, + schemaId, + className, + propertyName, + 2, + 2, + ); + const expectedMetadataValue0 = [100 + 2 * 0, 200 + 3 * 0.0, 300 + 4 * 0]; + const expectedMetadataValue1 = [ + 100 + 2 * 0.5, + 200 + 3 * 0.0, + 300 + 4 * 0.5, + ]; + const expectedMetadataValue2 = [ + 100 + 2 * 1.0, + 200 + 3 * 0.0, + 300 + 4 * 1.0, + ]; + + expect(actualMetadataValue0).toEqualEpsilon( + expectedMetadataValue0, + propertyValueEpsilon, + ); + expect(actualMetadataValue1).toEqualEpsilon( + expectedMetadataValue1, + propertyValueEpsilon, + ); + expect(actualMetadataValue2).toEqualEpsilon( + expectedMetadataValue2, + propertyValueEpsilon, + ); + scene.destroyForSpecs(); + }); + */ + it("picks UINT8 VEC2 from a property texture", async function () { if (webglStub) { return; @@ -3446,9 +3757,13 @@ describe( const propertyName = "example_normalized_UINT8_VEC2"; const classPropertyOffset = undefined; const classPropertyScale = undefined; + const metadataPropertyOffset = undefined; + const metadataPropertyScale = undefined; const gltf = createPropertyTextureGltfNormalizedVec2( classPropertyOffset, classPropertyScale, + metadataPropertyOffset, + metadataPropertyScale, ); const canvasSizeX = textureSizeX * canvasScaling; @@ -3510,7 +3825,7 @@ describe( scene.destroyForSpecs(); }); - it("picks normalized UINT8 VEC2 with offset and scale from a property texture", async function () { + it("picks normalized UINT8 VEC2 from a property texture with offset and scale in class property", async function () { if (webglStub) { return; } @@ -3518,11 +3833,15 @@ describe( const schemaId = undefined; const className = "exampleClass"; const propertyName = "example_normalized_UINT8_VEC2"; - const classPropertyOffset = [100, 200]; - const classPropertyScale = [2, 3]; + const classPropertyOffset = [100.0, 200.0]; + const classPropertyScale = [2.0, 3.0]; + const metadataPropertyOffset = undefined; + const metadataPropertyScale = undefined; const gltf = createPropertyTextureGltfNormalizedVec2( classPropertyOffset, classPropertyScale, + metadataPropertyOffset, + metadataPropertyScale, ); const canvasSizeX = textureSizeX * canvasScaling; @@ -3593,6 +3912,94 @@ describe( scene.destroyForSpecs(); }); + it("picks normalized UINT8 VEC2 from a property texture with offset and scale in property texture property", async function () { + if (webglStub) { + return; + } + + const schemaId = undefined; + const className = "exampleClass"; + const propertyName = "example_normalized_UINT8_VEC2"; + const classPropertyOffset = [100.0, 200.0]; + const classPropertyScale = [2.0, 3.0]; + // These should override the values from the class property: + const metadataPropertyOffset = [300.0, 400.0]; + const metadataPropertyScale = [4.0, 5.0]; + const gltf = createPropertyTextureGltfNormalizedVec2( + classPropertyOffset, + classPropertyScale, + metadataPropertyOffset, + metadataPropertyScale, + ); + + const canvasSizeX = textureSizeX * canvasScaling; + const canvasSizeY = textureSizeY * canvasScaling; + const scene = createScene({ + canvas: createCanvas(canvasSizeX, canvasSizeY), + contextOptions: { + requestWebgl1: true, + }, + }); + + await loadAsModel(scene, gltf); + fitCameraToUnitSquare(scene.camera); + + scene.initializeFrame(); + scene.render(defaultDate); + + const actualMetadataValue0 = pickMetadataAt( + scene, + schemaId, + className, + propertyName, + 0, + 0, + ); + const actualMetadataValue1 = pickMetadataAt( + scene, + schemaId, + className, + propertyName, + 1, + 1, + ); + const actualMetadataValue2 = pickMetadataAt( + scene, + schemaId, + className, + propertyName, + 2, + 2, + ); + + const expectedMetadataValue0 = new Cartesian2( + metadataPropertyOffset[0] + metadataPropertyScale[0] * 0.0, + metadataPropertyOffset[1] + metadataPropertyScale[1] * 0.0, + ); + const expectedMetadataValue1 = new Cartesian2( + metadataPropertyOffset[0] + metadataPropertyScale[0] * 0.5, + metadataPropertyOffset[1] + metadataPropertyScale[1] * 0.0, + ); + const expectedMetadataValue2 = new Cartesian2( + metadataPropertyOffset[0] + metadataPropertyScale[0] * 1.0, + metadataPropertyOffset[1] + metadataPropertyScale[1] * 0.0, + ); + + expect(actualMetadataValue0).toEqualEpsilon( + expectedMetadataValue0, + propertyValueEpsilon, + ); + expect(actualMetadataValue1).toEqualEpsilon( + expectedMetadataValue1, + propertyValueEpsilon, + ); + expect(actualMetadataValue2).toEqualEpsilon( + expectedMetadataValue2, + propertyValueEpsilon, + ); + scene.destroyForSpecs(); + }); + it("picks UINT8 VEC3 from a property texture", async function () { if (webglStub) { return; From 5d0896616974ab98246050f5bbee40a0ea923a48 Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Thu, 10 Oct 2024 18:11:23 +0200 Subject: [PATCH 007/175] Omit spec with error from unrelated issue --- packages/engine/Specs/Scene/SceneSpec.js | 90 +----------------------- 1 file changed, 1 insertion(+), 89 deletions(-) diff --git a/packages/engine/Specs/Scene/SceneSpec.js b/packages/engine/Specs/Scene/SceneSpec.js index 68a2bd1bbb1c..02651563bf39 100644 --- a/packages/engine/Specs/Scene/SceneSpec.js +++ b/packages/engine/Specs/Scene/SceneSpec.js @@ -244,7 +244,7 @@ function createEmbeddedGltfWithPropertyTexture( */ function createPropertyTextureGltf(schema, properties) { const gltf = createEmbeddedGltfWithPropertyTexture(schema, properties); - /*/ + //*/ // Copy-and-paste this into a file to have the actual glTF: console.log("SPEC GLTF:"); console.log("-".repeat(80)); @@ -3591,94 +3591,6 @@ describe( scene.destroyForSpecs(); }); - // XXX TODO Shader compilation error that seems to be unrelated - // to metadata picking. Test this in isolation! - /* - it("picks fixed length normalized UINT8 SCALAR array from a property texture with offset and scale in class property", async function () { - if (webglStub) { - return; - } - const schemaId = undefined; - const className = "exampleClass"; - const propertyName = "example_fixed_length_normalized_UINT8_SCALAR_array"; - const classPropertyOffset = [100, 200, 300 ]; - const classPropertyScale = [2, 3, 4]; - const metadataPropertyOffset = undefined; - const metadataPropertyScale = undefined; - const gltf = createPropertyTextureGltfNormalizedScalarArray( - classPropertyOffset, - classPropertyScale, - metadataPropertyOffset, - metadataPropertyScale, - ); - - const canvasSizeX = textureSizeX * canvasScaling; - const canvasSizeY = textureSizeY * canvasScaling; - const scene = createScene({ - canvas: createCanvas(canvasSizeX, canvasSizeY), - contextOptions: { - requestWebgl1: true, - }, - }); - - await loadAsModel(scene, gltf); - fitCameraToUnitSquare(scene.camera); - - scene.initializeFrame(); - scene.render(defaultDate); - - const actualMetadataValue0 = pickMetadataAt( - scene, - schemaId, - className, - propertyName, - 0, - 0, - ); - const actualMetadataValue1 = pickMetadataAt( - scene, - schemaId, - className, - propertyName, - 1, - 1, - ); - const actualMetadataValue2 = pickMetadataAt( - scene, - schemaId, - className, - propertyName, - 2, - 2, - ); - const expectedMetadataValue0 = [100 + 2 * 0, 200 + 3 * 0.0, 300 + 4 * 0]; - const expectedMetadataValue1 = [ - 100 + 2 * 0.5, - 200 + 3 * 0.0, - 300 + 4 * 0.5, - ]; - const expectedMetadataValue2 = [ - 100 + 2 * 1.0, - 200 + 3 * 0.0, - 300 + 4 * 1.0, - ]; - - expect(actualMetadataValue0).toEqualEpsilon( - expectedMetadataValue0, - propertyValueEpsilon, - ); - expect(actualMetadataValue1).toEqualEpsilon( - expectedMetadataValue1, - propertyValueEpsilon, - ); - expect(actualMetadataValue2).toEqualEpsilon( - expectedMetadataValue2, - propertyValueEpsilon, - ); - scene.destroyForSpecs(); - }); - */ - it("picks UINT8 VEC2 from a property texture", async function () { if (webglStub) { return; From bf533875f3ecc8d12c79726ffa1bb377f49fc2d0 Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Thu, 10 Oct 2024 18:11:48 +0200 Subject: [PATCH 008/175] TSDoc fixes --- packages/engine/Source/Scene/DerivedCommand.js | 10 ++++++---- packages/engine/Source/Scene/PickedMetadataInfo.js | 9 ++++++++- packages/engine/Source/Scene/Scene.js | 2 +- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/packages/engine/Source/Scene/DerivedCommand.js b/packages/engine/Source/Scene/DerivedCommand.js index 76e1fcae669c..bb091a105e46 100644 --- a/packages/engine/Source/Scene/DerivedCommand.js +++ b/packages/engine/Source/Scene/DerivedCommand.js @@ -449,8 +449,9 @@ function unnormalize(input, componentType) { * property, normalized to the range [0, 1]. * * @param {MetadataClassProperty} classProperty The class property - * @param {PropertyTextureProperty|PropertyAttributeProperty} metadataProperty The metadata property - * @returns The string + * @param {object} metadataProperty The metadata property, either + * a `PropertyTextureProperty` or a `PropertyAttributeProperty` + * @returns {string} The string */ function getSourceValueStringScalar(classProperty, metadataProperty) { let result = `float(value)`; @@ -475,9 +476,10 @@ function getSourceValueStringScalar(classProperty, metadataProperty) { * component of the given property, normalized to the range [0, 1]. * * @param {MetadataClassProperty} classProperty The class property - * @param {PropertyTextureProperty|PropertyAttributeProperty} metadataProperty The metadata property + * @param {object} metadataProperty The metadata property, either + * a `PropertyTextureProperty` or a `PropertyAttributeProperty` * @param {string} componentName The name, in ["x", "y", "z", "w"] - * @returns The string + * @returns {string} The string */ function getSourceValueStringComponent( classProperty, diff --git a/packages/engine/Source/Scene/PickedMetadataInfo.js b/packages/engine/Source/Scene/PickedMetadataInfo.js index 3107b22e4d0a..8a80e3842e1e 100644 --- a/packages/engine/Source/Scene/PickedMetadataInfo.js +++ b/packages/engine/Source/Scene/PickedMetadataInfo.js @@ -50,7 +50,14 @@ function PickedMetadataInfo( * property texture or property attribute of the `StructuralMetadata` * that matches the class name and property name. * - * @type {PropertyTextureProperty|PropertyAttributeProperty} + * The `PropertyTextureProperty` and `PropertyAttributeProperty` + * types are not public and do not share a common base type. + * But they both define the getters + * - `hasValueTransform` (`boolean`) + * - `offset` (`any`) + * - `scale` (`any`) + * + * @type {object} */ this.metadataProperty = metadataProperty; } diff --git a/packages/engine/Source/Scene/Scene.js b/packages/engine/Source/Scene/Scene.js index 3cf1b4b9bcec..20b9aa1c40dd 100644 --- a/packages/engine/Source/Scene/Scene.js +++ b/packages/engine/Source/Scene/Scene.js @@ -4403,7 +4403,7 @@ Scene.prototype.pickVoxel = function (windowPosition, width, height) { * values from * @param {string} propertyName The name of the metadata property to pick * values from - * @returns The metadata value + * @returns {any} The metadata value * * @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy. */ From 198f06a5cc93584b20a2a2dcd157055d1e2e6172 Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Thu, 10 Oct 2024 18:13:08 +0200 Subject: [PATCH 009/175] Remove debug output --- packages/engine/Specs/Scene/SceneSpec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/engine/Specs/Scene/SceneSpec.js b/packages/engine/Specs/Scene/SceneSpec.js index 02651563bf39..3e5c7dda20ba 100644 --- a/packages/engine/Specs/Scene/SceneSpec.js +++ b/packages/engine/Specs/Scene/SceneSpec.js @@ -244,7 +244,7 @@ function createEmbeddedGltfWithPropertyTexture( */ function createPropertyTextureGltf(schema, properties) { const gltf = createEmbeddedGltfWithPropertyTexture(schema, properties); - //*/ + /*/ // Copy-and-paste this into a file to have the actual glTF: console.log("SPEC GLTF:"); console.log("-".repeat(80)); From b99daf9aa62fcd30cff0e6e11f9bd78c28f6e962 Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Thu, 10 Oct 2024 20:28:25 +0200 Subject: [PATCH 010/175] Removed debug logs --- packages/engine/Source/Scene/DerivedCommand.js | 6 ------ packages/engine/Source/Scene/Scene.js | 4 ---- 2 files changed, 10 deletions(-) diff --git a/packages/engine/Source/Scene/DerivedCommand.js b/packages/engine/Source/Scene/DerivedCommand.js index bb091a105e46..8e3478fc65f3 100644 --- a/packages/engine/Source/Scene/DerivedCommand.js +++ b/packages/engine/Source/Scene/DerivedCommand.js @@ -576,12 +576,6 @@ function getPickMetadataShaderProgram( } } - // XXX_DEBUG - for (let i = 0; i < componentCount; i++) { - console.log(`At ${i} source value string is "${sourceValueStrings[i]}"`); - } - // XXX_DEBUG - const newDefines = shaderProgram.fragmentShaderSource.defines.slice(); newDefines.push(MetadataPickingPipelineStage.METADATA_PICKING_ENABLED); diff --git a/packages/engine/Source/Scene/Scene.js b/packages/engine/Source/Scene/Scene.js index 20b9aa1c40dd..0a2543ef7fd1 100644 --- a/packages/engine/Source/Scene/Scene.js +++ b/packages/engine/Source/Scene/Scene.js @@ -4450,10 +4450,6 @@ Scene.prototype.pickMetadata = function ( return undefined; } - // XXX DEBUG - console.log("Using metadata property ", metadataProperty); - // XXX DEBUG - const pickedMetadataInfo = new PickedMetadataInfo( schemaId, className, From d546d394fd04bb547a35d1b515d4ff90f7934558 Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Thu, 10 Oct 2024 20:29:13 +0200 Subject: [PATCH 011/175] Integrate offset/scale in class property --- .../Extensions/Gpm/GltfMeshPrimitiveGpmLoader.js | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfMeshPrimitiveGpmLoader.js b/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfMeshPrimitiveGpmLoader.js index 7be022144311..b3ef4f258ed7 100644 --- a/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfMeshPrimitiveGpmLoader.js +++ b/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfMeshPrimitiveGpmLoader.js @@ -268,9 +268,9 @@ GltfMeshPrimitiveGpmLoader._createPpeTextureClassJson = function ( // property values when they are `normalized`, the values will be // declared as `normalized` here. // The normalization factor will later have to be cancelled out, - // when integrating the `scale` into the actual property texture - // property. In the property texture property, the `scale` has to - // be multiplied by 255. + // with the `scale` being multiplied by 255. + const offset = ppeTexture.offset ?? 0.0; + const scale = (ppeTexture.scale ?? 1.0) * 255.0; const classJson = { name: `PPE texture class ${index}`, properties: { @@ -279,6 +279,8 @@ GltfMeshPrimitiveGpmLoader._createPpeTextureClassJson = function ( type: "SCALAR", componentType: "UINT8", normalized: true, + offset: offset, + scale: scale, min: traits.min, max: traits.max, }, @@ -406,19 +408,12 @@ GltfMeshPrimitiveGpmLoader._convertToStructuralMetadata = function ( const ppePropertyName = traits.source; const metadataClass = ppeTexturesMetadataSchema.classes[classId]; - // The class property has been declared as `normalized`, so - // that `offset` and `scale` can be applied. The normalization - // factor has to be cancelled out here, by multiplying the - // `scale` with 255. - const scale = (ppeTexture.scale ?? 1.0) * 255.0; const ppeTextureAsPropertyTexture = { class: classId, properties: { [ppePropertyName]: { index: ppeTexture.index, texCoord: ppeTexture.texCoord, - offset: ppeTexture.offset, - scale: scale, }, }, }; From ffd67c3bd455c890ebab17cccdeab056fba7c481 Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Fri, 11 Oct 2024 17:32:43 +0200 Subject: [PATCH 012/175] Update CHANGES.md --- CHANGES.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 806d9b219076..5d1633a7dc57 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -8,6 +8,10 @@ - Added `ScreenSpaceCameraController.maximumTiltAngle` to limit how much the camera can tilt. [#12169](https://github.com/CesiumGS/cesium/pull/12169) +##### Fixes :wrench: + +- Properly handle `offset` and `scale` properties when picking metadata from property textures. [#12237](https://github.com/CesiumGS/cesium/pull/12237) + ### 1.122 - 2024-10-01 #### @cesium/engine From 1a7dbb64171282d6a330d02b0156df262b88987e Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Sat, 12 Oct 2024 14:23:03 +0200 Subject: [PATCH 013/175] Do not try to pick property attributes --- .../Source/Scene/getMetadataProperty.js | 32 +++++++------------ 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/packages/engine/Source/Scene/getMetadataProperty.js b/packages/engine/Source/Scene/getMetadataProperty.js index 1d5e8ea246f0..291f4f639851 100644 --- a/packages/engine/Source/Scene/getMetadataProperty.js +++ b/packages/engine/Source/Scene/getMetadataProperty.js @@ -1,28 +1,24 @@ import defined from "../Core/defined.js"; /** - * Return the `PropertyTextureProperty` or `PropertyAttributeProperty` from - * the given `StructuralMetadata` that matches the given description. + * Return the `PropertyTextureProperty` from the given `StructuralMetadata` + * that matches the given description. * * If the given structural metadata is `undefined`, then `undefined` is returned. * - * Otherwise, this method will check all the property textures and property - * attributes in the given structural metadata. + * Otherwise, this method will check all the property textures in the given + * structural metadata. * * If it finds a property texture that has a class with an `_id` that matches * the given name, and that contains a property for the given property name, then * this property is returned. * - * If it finds a property attribute that has a class with an `_id` that matches - * the given name, and that contains a property for the given property name, then - * this property is returned. - * * Otherwise, `undefined` is returned * * @param {StructuralMetadata} structuralMetadata The structural metadata * @param {string} className The name of the metadata class * @param {string} propertyName The name of the metadata property - * @returns {PropertyTextureProperty|PropertyAttributeProperty|undefined} + * @returns {PropertyTextureProperty|undefined} * @private */ function getMetadataProperty(structuralMetadata, className, propertyName) { @@ -40,17 +36,13 @@ function getMetadataProperty(structuralMetadata, className, propertyName) { } } } - const propertyAttributes = structuralMetadata.propertyAttributes; - for (const propertyAttribute of propertyAttributes) { - const metadataClass = propertyAttribute.class; - if (metadataClass.id === className) { - const properties = propertyAttribute.properties; - const property = properties[propertyName]; - if (defined(property)) { - return property; - } - } - } + // Note: This could check for property attributes in a similar + // way. But since picking arbitrary property attributes via the + // frame buffer is not supported yet, returning "undefined" here + // causes the picking to bail out early and safely when no + // property texture was found. + // See https://github.com/CesiumGS/cesium/issues/12225 + return undefined; } export default getMetadataProperty; From e2de7f2dadb009a32574f1db4d084a887c1e885a Mon Sep 17 00:00:00 2001 From: s3xysteak Date: Tue, 15 Oct 2024 00:31:09 +0800 Subject: [PATCH 014/175] feat: `SampledProperty` introduce `getSample` to get the time of samples by index --- .../Source/DataSources/SampledProperty.js | 24 +++++++++++++++++++ .../Specs/DataSources/SampledPropertySpec.js | 22 +++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/packages/engine/Source/DataSources/SampledProperty.js b/packages/engine/Source/DataSources/SampledProperty.js index 803555188725..ef6b6ee60df2 100644 --- a/packages/engine/Source/DataSources/SampledProperty.js +++ b/packages/engine/Source/DataSources/SampledProperty.js @@ -674,6 +674,30 @@ SampledProperty.prototype.addSamples = function ( this._definitionChanged.raiseEvent(this); }; +/** + * Gets the time of the sample. Negative for backward. + * + * @param {number} index The index of samples list. + * @returns {JulianDate | undefined} The JulianDate time of the sample, or undefined if failed. + */ +SampledProperty.prototype.getSample = function (index) { + //>>includeStart('debug', pragmas.debug); + Check.defined("index", index); + //>>includeEnd('debug'); + + const times = this._times; + const len = times.length; + if (!len) { + return undefined; + } + + if (index < 0) { + index += len; + } + + return times[index]; +}; + /** * Adds samples as a single packed array where each new sample is represented as a date, * followed by the packed representation of the corresponding value and derivatives. diff --git a/packages/engine/Specs/DataSources/SampledPropertySpec.js b/packages/engine/Specs/DataSources/SampledPropertySpec.js index 3caa5d99fe6c..e6ee6edc9335 100644 --- a/packages/engine/Specs/DataSources/SampledPropertySpec.js +++ b/packages/engine/Specs/DataSources/SampledPropertySpec.js @@ -110,6 +110,28 @@ describe("DataSources/SampledProperty", function () { expect(property.getValue(new JulianDate(0.5, 0))).toEqual(7.5); }); + it("getSample works", function () { + const times = [ + new JulianDate(0, 0), + new JulianDate(1, 0), + new JulianDate(2, 0), + ]; + const values = [7, 8, 9]; + + const property = new SampledProperty(Number); + property.addSamples(times, values); + + expect(property.getSample(0)).toEqual(times[0]); + expect(property.getSample(1)).toEqual(times[1]); + expect(property.getSample(2)).toEqual(times[2]); + expect(property.getSample(3)).toBe(undefined); + + expect(property.getSample(-1)).toBe(times[2]); + expect(property.getSample(-2)).toBe(times[1]); + expect(property.getSample(-3)).toBe(times[0]); + expect(property.getSample(-4)).toBe(undefined); + }); + it("can remove a sample at a date", function () { const times = [ new JulianDate(0, 0), From 586c09f59d6c048d574509e840701dd1aa7347d2 Mon Sep 17 00:00:00 2001 From: s3xysteak Date: Tue, 15 Oct 2024 19:27:06 +0800 Subject: [PATCH 015/175] docs: update changes. --- CHANGES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.md b/CHANGES.md index 806d9b219076..9cc3586a03b9 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -6,6 +6,7 @@ ##### Additions :tada: +- Added `getSample` to `SampledProperty` to get the time of samples. [#12253](https://github.com/CesiumGS/cesium/pull/12253) - Added `ScreenSpaceCameraController.maximumTiltAngle` to limit how much the camera can tilt. [#12169](https://github.com/CesiumGS/cesium/pull/12169) ### 1.122 - 2024-10-01 From 288bfc934d4b81518ed0da39e36b55069743e18f Mon Sep 17 00:00:00 2001 From: Jeshurun Hembd Date: Tue, 15 Oct 2024 14:03:40 -0400 Subject: [PATCH 016/175] Normalize ambient occlusion by accumulated window weights --- .../PostProcessStages/AmbientOcclusionGenerate.glsl | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/packages/engine/Source/Shaders/PostProcessStages/AmbientOcclusionGenerate.glsl b/packages/engine/Source/Shaders/PostProcessStages/AmbientOcclusionGenerate.glsl index 2b4d44495a98..8975807958da 100644 --- a/packages/engine/Source/Shaders/PostProcessStages/AmbientOcclusionGenerate.glsl +++ b/packages/engine/Source/Shaders/PostProcessStages/AmbientOcclusionGenerate.glsl @@ -83,6 +83,7 @@ void main(void) sampleDirection = rotateStep * sampleDirection; float localAO = 0.0; + float accumulatedWindowWeights = 0.0; vec2 radialStep = stepSize * sampleDirection; for (int j = 0; j < RADIAL_STEPS; j++) @@ -96,10 +97,16 @@ void main(void) break; } + // Compute distance along geometry, for weighting AO contribution vec3 stepPositionEC = pixelToEye(newCoords); vec3 stepVector = stepPositionEC - positionEC; float stepLength = length(stepVector); + // Compute lateral distance from output point, for weight normalization + vec3 inPlaneStepEC = vec3(stepPositionEC.x, stepPositionEC.y, positionEC.z); + vec3 windowVector = inPlaneStepEC - positionEC; + float windowLength = length(windowVector); + float dotVal = clamp(dot(normalEC, normalize(stepVector)), 0.0, 1.0); if (dotVal < bias) { @@ -108,11 +115,13 @@ void main(void) float weight = gaussian(stepLength, samplingRadius); localAO += weight * dotVal; + // TODO: This is slow! Better to analytically compute window scales + accumulatedWindowWeights += gaussian(windowLength, samplingRadius); } - ao += localAO; + ao += 24.0 * localAO / accumulatedWindowWeights; } - ao *= angleStepScale * radialStepScale * sqrt(-positionEC.z); + ao *= angleStepScale * radialStepScale; ao = 1.0 - clamp(ao, 0.0, 1.0); ao = pow(ao, intensity); out_FragColor = vec4(vec3(ao), 1.0); From b4e5af441bce29cf58800c8e80c06b14477d59b1 Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Wed, 16 Oct 2024 16:02:27 +0200 Subject: [PATCH 017/175] Update GPM visualization Sandcastle --- .../3D Tiles NGA GPM Visualization.html | 1352 +++++++++++++++++ .../3D Tiles NGA GPM Visualization.jpg | Bin 0 -> 8429 bytes 2 files changed, 1352 insertions(+) create mode 100644 Apps/Sandcastle/gallery/3D Tiles NGA GPM Visualization.html create mode 100644 Apps/Sandcastle/gallery/3D Tiles NGA GPM Visualization.jpg diff --git a/Apps/Sandcastle/gallery/3D Tiles NGA GPM Visualization.html b/Apps/Sandcastle/gallery/3D Tiles NGA GPM Visualization.html new file mode 100644 index 000000000000..0a5c679375af --- /dev/null +++ b/Apps/Sandcastle/gallery/3D Tiles NGA GPM Visualization.html @@ -0,0 +1,1352 @@ + + + + + + + + + Cesium Demo + + + + + + + +
+
+

Loading...

+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+

Cesium GPM Visualization

+
Data set + + + +
Anchor point ellipsoid + + + +
Shader mode + +
+
+ Uncertainty information is stored as GPM metadata in the tiles.
+
+ The anchor points provide information about the low-frequency + error.
+ This error is visualized as ellipsoids.
+
+ The high-frequency error is represented as + Per-Point Error textures.
+ This error can be visualized with custom shaders. The 'uncertainty'
+ shaders visualize the horizontal or vertical uncertainty with a color
+ scale. The 'threshold' shaders highlight areas where the selected
+ error threshold is exceeded. Picking a point on the tileset will show
+ a label with the actual error values. +
+
+ + + diff --git a/Apps/Sandcastle/gallery/3D Tiles NGA GPM Visualization.jpg b/Apps/Sandcastle/gallery/3D Tiles NGA GPM Visualization.jpg new file mode 100644 index 0000000000000000000000000000000000000000..5b2daea52ce03c25b298593a0d15c500b66cbe60 GIT binary patch literal 8429 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 g*D-aQhD6-}7cC(sse* Date: Wed, 16 Oct 2024 16:09:18 +0200 Subject: [PATCH 018/175] Minor typo fixes --- Apps/Sandcastle/gallery/3D Tiles NGA GPM Visualization.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Apps/Sandcastle/gallery/3D Tiles NGA GPM Visualization.html b/Apps/Sandcastle/gallery/3D Tiles NGA GPM Visualization.html index 0a5c679375af..9c79f18dcdb0 100644 --- a/Apps/Sandcastle/gallery/3D Tiles NGA GPM Visualization.html +++ b/Apps/Sandcastle/gallery/3D Tiles NGA GPM Visualization.html @@ -624,7 +624,7 @@

Cesium GPM Visualization

* create an option to select default shading, a 1D shader, * or a 2D shader * - * @param {string} title The title to be displayed in the combox box + * @param {string} title The title to be displayed in the combo box * @param {string|undefined} propertyName0 The property name 0 * @param {number} sourceMin0 The minimum source value 0 * @param {number} sourceMax0 The maximum source value 0 @@ -677,8 +677,8 @@

Cesium GPM Visualization

* It will allow selecting a "threshold shader" for the specified * properties. * - * @param {string} title The title to be displayed in the combox box - * @param {string} title The title to be displayed in the combox box + * @param {string} title The title to be displayed in the combo box + * @param {string} title The title to be displayed in the combo box * @param {string|undefined} propertyName0 The property name 0 * @param {number} sourceMin0 The minimum source value 0 * @param {number} sourceMax0 The maximum source value 0 From f82441059e8b925ae77d1a04d1b7115e8db54574 Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Tue, 29 Oct 2024 13:36:12 +0100 Subject: [PATCH 019/175] Add JDoc for parameter --- packages/engine/Source/Scene/MetadataClassProperty.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/engine/Source/Scene/MetadataClassProperty.js b/packages/engine/Source/Scene/MetadataClassProperty.js index 5683d5c8501e..b51a79884817 100644 --- a/packages/engine/Source/Scene/MetadataClassProperty.js +++ b/packages/engine/Source/Scene/MetadataClassProperty.js @@ -1160,6 +1160,9 @@ function normalizeInPlace(values, valueType, normalizeFunction) { * @param {number|number[]|number[][]} values The input values * @param {number|number[]|number[][]} offsets The offsets * @param {number|number[]|number[][]} scales The scales + * @param {Function} transformationFunction The function with the signature + * `(value:number, offset:number, scale:number) : number` that will be + * applied to the innermost elements * @returns The input values (or the result of applying the transformation * function to a single value if the values have not been an array). * @private From c4c945371556828efa6cc79e90a4b4988d670a9b Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Tue, 29 Oct 2024 13:37:38 +0100 Subject: [PATCH 020/175] Throw error for invalid type --- packages/engine/Source/Scene/MetadataPicking.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/engine/Source/Scene/MetadataPicking.js b/packages/engine/Source/Scene/MetadataPicking.js index 6972bd51f2d5..3e5068b09b4b 100644 --- a/packages/engine/Source/Scene/MetadataPicking.js +++ b/packages/engine/Source/Scene/MetadataPicking.js @@ -300,7 +300,7 @@ MetadataPicking.convertFromObjectType = function (type, value) { return Matrix4.unpack(value, Array(16)); } // Should never happen: - return value; + throw new RuntimeError(`Invalid metadata object type: ${type}`); }; /** From 5c64039035feaa3c2d886020b921fb89cd8b6a61 Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Tue, 29 Oct 2024 14:58:11 +0100 Subject: [PATCH 021/175] Fix copy and paste error. Handle endianness. JSDoc. --- .../engine/Source/Scene/MetadataPicking.js | 30 ++++++++++++------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/packages/engine/Source/Scene/MetadataPicking.js b/packages/engine/Source/Scene/MetadataPicking.js index 3e5068b09b4b..5aa1259330f9 100644 --- a/packages/engine/Source/Scene/MetadataPicking.js +++ b/packages/engine/Source/Scene/MetadataPicking.js @@ -30,6 +30,10 @@ const MetadataPicking = {}; * @param {DataView} dataView The data view * @param {number} index The index (byte offset) * @returns {number|bigint|undefined} The value + * @throws RuntimeError If the given component type is not a valid + * `MetadataComponentType` + * @throws RangeError If reading the data from the given data view would + * cause an out-of-bounds access * * @private */ @@ -44,21 +48,21 @@ MetadataPicking.decodeRawMetadataValue = function ( case MetadataComponentType.UINT8: return dataView.getUint8(index); case MetadataComponentType.INT16: - return dataView.getInt16(index); + return dataView.getInt16(index, true); case MetadataComponentType.UINT16: - return dataView.getUint16(index); + return dataView.getUint16(index, true); case MetadataComponentType.INT32: - return dataView.getInt32(index); + return dataView.getInt32(index, true); case MetadataComponentType.UINT32: - return dataView.getUint32(index); + return dataView.getUint32(index, true); case MetadataComponentType.INT64: - return dataView.getBigInt64(index); + return dataView.getBigInt64(index, true); case MetadataComponentType.UINT64: - return dataView.getBigUint64(index); + return dataView.getBigUint64(index, true); case MetadataComponentType.FLOAT32: - return dataView.getFloat32(index); + return dataView.getFloat32(index, true); case MetadataComponentType.FLOAT64: - return dataView.getFloat64(index); + return dataView.getFloat64(index, true); } throw new RuntimeError(`Invalid component type: ${componentType}`); }; @@ -78,6 +82,10 @@ MetadataPicking.decodeRawMetadataValue = function ( * @param {number} dataViewOffset The byte offset within the data view from * which the component should be read * @returns {number|bigint|undefined} The metadata value component + * @throws RuntimeError If the component o the given property is not + * a valid `MetadataComponentType` + * @throws RangeError If reading the data from the given data view would + * cause an out-of-bounds access */ MetadataPicking.decodeRawMetadataValueComponent = function ( classProperty, @@ -293,11 +301,11 @@ MetadataPicking.convertFromObjectType = function (type, value) { case MetadataType.VEC4: return Cartesian4.pack(value, Array(4)); case MetadataType.MAT2: - return Matrix2.unpack(value, Array(4)); + return Matrix2.pack(value, Array(4)); case MetadataType.MAT3: - return Matrix3.unpack(value, Array(9)); + return Matrix3.pack(value, Array(9)); case MetadataType.MAT4: - return Matrix4.unpack(value, Array(16)); + return Matrix4.pack(value, Array(16)); } // Should never happen: throw new RuntimeError(`Invalid metadata object type: ${type}`); From 225be9c85bf31d984bc6398dad1ff6a2b2a02984 Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Tue, 29 Oct 2024 14:58:33 +0100 Subject: [PATCH 022/175] First part of MetadataPickingSpec --- .../engine/Specs/Scene/MetadataPickingSpec.js | 616 ++++++++++++++++++ 1 file changed, 616 insertions(+) create mode 100644 packages/engine/Specs/Scene/MetadataPickingSpec.js diff --git a/packages/engine/Specs/Scene/MetadataPickingSpec.js b/packages/engine/Specs/Scene/MetadataPickingSpec.js new file mode 100644 index 000000000000..e17bebb2677b --- /dev/null +++ b/packages/engine/Specs/Scene/MetadataPickingSpec.js @@ -0,0 +1,616 @@ +import { MetadataComponentType, MetadataPicking } from "../../index.js"; + +// The precision for Jasmine `toBeCloseTo` calls +const precision = 3; + +describe("Scene/MetadataPicking", function () { + describe("decodeRawMetadataValue", function () { + it("throws for invalid component type", function () { + expect(function () { + const componentType = "NOT_A_COMPONENT_TYPE"; + const array = new Uint8Array([12, 23]); + const dataView = new DataView( + array.buffer, + array.byteOffset, + array.byteLength, + ); + const index = 0; + MetadataPicking.decodeRawMetadataValue(componentType, dataView, index); + }).toThrowError(); + }); + + it("throws for invalid index", function () { + expect(function () { + const componentType = MetadataComponentType.INT8; + const array = new Uint8Array([12, 23]); + const dataView = new DataView( + array.buffer, + array.byteOffset, + array.byteLength, + ); + const index = 1234; + MetadataPicking.decodeRawMetadataValue(componentType, dataView, index); + }).toThrowError(); + }); + + it("decodes raw value with component type INT8", function () { + const array = new Int8Array([12, 23]); + const componentType = MetadataComponentType.INT8; + const dataView = new DataView( + array.buffer, + array.byteOffset, + array.byteLength, + ); + const index = 1; + const value = MetadataPicking.decodeRawMetadataValue( + componentType, + dataView, + index, + ); + expect(value).toBe(23); + }); + + it("decodes raw value with component type UINT8", function () { + const array = new Uint8Array([12, 23]); + const componentType = MetadataComponentType.UINT8; + const dataView = new DataView( + array.buffer, + array.byteOffset, + array.byteLength, + ); + const index = 1; + const value = MetadataPicking.decodeRawMetadataValue( + componentType, + dataView, + index, + ); + expect(value).toBe(23); + }); + + it("decodes raw value with component type INT16", function () { + const array = new Int16Array([1234, 2345]); + const componentType = MetadataComponentType.INT16; + const dataView = new DataView( + array.buffer, + array.byteOffset, + array.byteLength, + ); + const index = 2; + const value = MetadataPicking.decodeRawMetadataValue( + componentType, + dataView, + index, + ); + expect(value).toBe(2345); + }); + + it("decodes raw value with component type UINT16", function () { + const array = new Uint16Array([1234, 2345]); + const componentType = MetadataComponentType.UINT16; + const dataView = new DataView( + array.buffer, + array.byteOffset, + array.byteLength, + ); + const index = 2; + const value = MetadataPicking.decodeRawMetadataValue( + componentType, + dataView, + index, + ); + expect(value).toBe(2345); + }); + + it("decodes raw value with component type INT32", function () { + const array = new Int32Array([123456, 234567]); + const componentType = MetadataComponentType.INT32; + const dataView = new DataView( + array.buffer, + array.byteOffset, + array.byteLength, + ); + const index = 4; + const value = MetadataPicking.decodeRawMetadataValue( + componentType, + dataView, + index, + ); + expect(value).toBe(234567); + }); + + it("decodes raw value with component type UINT32", function () { + const array = new Uint32Array([123456, 234567]); + const componentType = MetadataComponentType.UINT32; + const dataView = new DataView( + array.buffer, + array.byteOffset, + array.byteLength, + ); + const index = 4; + const value = MetadataPicking.decodeRawMetadataValue( + componentType, + dataView, + index, + ); + expect(value).toBe(234567); + }); + + it("decodes raw value with component type INT64", function () { + const array = new BigInt64Array([12345678900n, 23456789000n]); + const componentType = MetadataComponentType.INT64; + const dataView = new DataView( + array.buffer, + array.byteOffset, + array.byteLength, + ); + const index = 8; + const value = MetadataPicking.decodeRawMetadataValue( + componentType, + dataView, + index, + ); + expect(value).toBe(23456789000n); + }); + + it("decodes raw value with component type UINT64", function () { + const array = new BigUint64Array([12345678900n, 23456789000n]); + const componentType = MetadataComponentType.UINT64; + const dataView = new DataView( + array.buffer, + array.byteOffset, + array.byteLength, + ); + const index = 8; + const value = MetadataPicking.decodeRawMetadataValue( + componentType, + dataView, + index, + ); + expect(value).toBe(23456789000n); + }); + + it("decodes raw value with component type FLOAT32", function () { + const array = new Float32Array([1.23, 2.34]); + const componentType = MetadataComponentType.FLOAT32; + const dataView = new DataView( + array.buffer, + array.byteOffset, + array.byteLength, + ); + const index = 4; + const value = MetadataPicking.decodeRawMetadataValue( + componentType, + dataView, + index, + ); + expect(value).toBeCloseTo(2.34, precision); + }); + + it("decodes raw value with component type FLOAT64", function () { + const array = new Float64Array([1.23, 2.34]); + const componentType = MetadataComponentType.FLOAT64; + const dataView = new DataView( + array.buffer, + array.byteOffset, + array.byteLength, + ); + const index = 8; + const value = MetadataPicking.decodeRawMetadataValue( + componentType, + dataView, + index, + ); + expect(value).toBeCloseTo(2.34, precision); + }); + }); + + describe("decodeRawMetadataValueComponent", function () { + it("throws for invalid component type", function () { + expect(function () { + const classProperty = { + componentType: "NOT_A_COMPONENT_TYPE", + normalized: false, + }; + const array = new Uint8Array([12, 23]); + const dataView = new DataView( + array.buffer, + array.byteOffset, + array.byteLength, + ); + const dataViewOffset = 0; + MetadataPicking.decodeRawMetadataValueComponent( + classProperty, + dataView, + dataViewOffset, + ); + }).toThrowError(); + }); + + it("throws for invalid offset", function () { + expect(function () { + const classProperty = { + componentType: MetadataComponentType.INT8, + normalized: false, + }; + const array = new Uint8Array([12, 23]); + const dataView = new DataView( + array.buffer, + array.byteOffset, + array.byteLength, + ); + const dataViewOffset = 1234; + MetadataPicking.decodeRawMetadataValueComponent( + classProperty, + dataView, + dataViewOffset, + ); + }).toThrowError(); + }); + + it("decodes component with type INT8", function () { + const classProperty = { + componentType: MetadataComponentType.INT8, + normalized: false, + }; + const array = new Uint8Array([12, 23]); + const dataView = new DataView( + array.buffer, + array.byteOffset, + array.byteLength, + ); + const dataViewOffset = 1; + const value = MetadataPicking.decodeRawMetadataValueComponent( + classProperty, + dataView, + dataViewOffset, + ); + expect(value).toBe(23); + }); + + it("decodes component with type normalized INT8", function () { + const classProperty = { + componentType: MetadataComponentType.INT8, + normalized: true, + }; + const array = new Uint8Array([12, 23]); + const dataView = new DataView( + array.buffer, + array.byteOffset, + array.byteLength, + ); + const dataViewOffset = 1; + const value = MetadataPicking.decodeRawMetadataValueComponent( + classProperty, + dataView, + dataViewOffset, + ); + expect(value).toBeCloseTo(23 / 127.0, precision); + }); + + it("decodes component with type UINT8", function () { + const classProperty = { + componentType: MetadataComponentType.UINT8, + normalized: false, + }; + const array = new Uint8Array([12, 23]); + const dataView = new DataView( + array.buffer, + array.byteOffset, + array.byteLength, + ); + const dataViewOffset = 1; + const value = MetadataPicking.decodeRawMetadataValueComponent( + classProperty, + dataView, + dataViewOffset, + ); + expect(value).toBe(23); + }); + + it("decodes component with type normalized UINT8", function () { + const classProperty = { + componentType: MetadataComponentType.UINT8, + normalized: true, + }; + const array = new Uint8Array([12, 23]); + const dataView = new DataView( + array.buffer, + array.byteOffset, + array.byteLength, + ); + const dataViewOffset = 1; + const value = MetadataPicking.decodeRawMetadataValueComponent( + classProperty, + dataView, + dataViewOffset, + ); + expect(value).toBeCloseTo(23 / 255.0, precision); + }); + + it("decodes component with type INT16", function () { + const classProperty = { + componentType: MetadataComponentType.INT16, + normalized: false, + }; + const array = new Int16Array([1234, 2345]); + const dataView = new DataView( + array.buffer, + array.byteOffset, + array.byteLength, + ); + const dataViewOffset = 2; + const value = MetadataPicking.decodeRawMetadataValueComponent( + classProperty, + dataView, + dataViewOffset, + ); + expect(value).toBe(2345); + }); + + it("decodes component with type normalized INT16", function () { + const classProperty = { + componentType: MetadataComponentType.INT16, + normalized: true, + }; + const array = new Int16Array([1234, 2345]); + const dataView = new DataView( + array.buffer, + array.byteOffset, + array.byteLength, + ); + const dataViewOffset = 2; + const value = MetadataPicking.decodeRawMetadataValueComponent( + classProperty, + dataView, + dataViewOffset, + ); + expect(value).toBeCloseTo(2345 / 32767.0, precision); + }); + + it("decodes component with type UINT16", function () { + const classProperty = { + componentType: MetadataComponentType.UINT16, + normalized: false, + }; + const array = new Uint16Array([1234, 2345]); + const dataView = new DataView( + array.buffer, + array.byteOffset, + array.byteLength, + ); + const dataViewOffset = 2; + const value = MetadataPicking.decodeRawMetadataValueComponent( + classProperty, + dataView, + dataViewOffset, + ); + expect(value).toBe(2345); + }); + + it("decodes component with type normalized UINT16", function () { + const classProperty = { + componentType: MetadataComponentType.UINT16, + normalized: true, + }; + const array = new Uint16Array([1234, 2345]); + const dataView = new DataView( + array.buffer, + array.byteOffset, + array.byteLength, + ); + const dataViewOffset = 2; + const value = MetadataPicking.decodeRawMetadataValueComponent( + classProperty, + dataView, + dataViewOffset, + ); + expect(value).toBeCloseTo(2345 / 65535.0, precision); + }); + + it("decodes component with type INT32", function () { + const classProperty = { + componentType: MetadataComponentType.INT32, + normalized: false, + }; + const array = new Int32Array([123456, 234567]); + const dataView = new DataView( + array.buffer, + array.byteOffset, + array.byteLength, + ); + const dataViewOffset = 4; + const value = MetadataPicking.decodeRawMetadataValueComponent( + classProperty, + dataView, + dataViewOffset, + ); + expect(value).toBe(234567); + }); + + it("decodes component with type normalized INT32", function () { + const classProperty = { + componentType: MetadataComponentType.INT32, + normalized: true, + }; + const array = new Int32Array([123456, 234567]); + const dataView = new DataView( + array.buffer, + array.byteOffset, + array.byteLength, + ); + const dataViewOffset = 4; + const value = MetadataPicking.decodeRawMetadataValueComponent( + classProperty, + dataView, + dataViewOffset, + ); + expect(value).toBeCloseTo(234567 / 2147483647.0, precision); + }); + + it("decodes component with type UINT32", function () { + const classProperty = { + componentType: MetadataComponentType.UINT32, + normalized: false, + }; + const array = new Uint32Array([123456, 234567]); + const dataView = new DataView( + array.buffer, + array.byteOffset, + array.byteLength, + ); + const dataViewOffset = 4; + const value = MetadataPicking.decodeRawMetadataValueComponent( + classProperty, + dataView, + dataViewOffset, + ); + expect(value).toBe(234567); + }); + + it("decodes component with type normalized UINT32", function () { + const classProperty = { + componentType: MetadataComponentType.UINT32, + normalized: true, + }; + const array = new Uint32Array([123456, 234567]); + const dataView = new DataView( + array.buffer, + array.byteOffset, + array.byteLength, + ); + const dataViewOffset = 4; + const value = MetadataPicking.decodeRawMetadataValueComponent( + classProperty, + dataView, + dataViewOffset, + ); + expect(value).toBeCloseTo(234567 / 4294967295.0, precision); + }); + + it("decodes component with type INT64", function () { + const classProperty = { + componentType: MetadataComponentType.INT64, + normalized: false, + }; + const array = new BigInt64Array([12345678900n, 23456789000n]); + const dataView = new DataView( + array.buffer, + array.byteOffset, + array.byteLength, + ); + const dataViewOffset = 8; + const value = MetadataPicking.decodeRawMetadataValueComponent( + classProperty, + dataView, + dataViewOffset, + ); + expect(value).toBe(23456789000n); + }); + + it("decodes component with type normalized INT64", function () { + const classProperty = { + componentType: MetadataComponentType.INT64, + normalized: true, + }; + const array = new BigInt64Array([12345678900n, 23456789000n]); + const dataView = new DataView( + array.buffer, + array.byteOffset, + array.byteLength, + ); + const dataViewOffset = 8; + const value = MetadataPicking.decodeRawMetadataValueComponent( + classProperty, + dataView, + dataViewOffset, + ); + expect(Number(value)).toBeCloseTo( + Number(23456789000n / 9223372036854775807n), + precision, + ); + }); + + it("decodes component with type UINT64", function () { + const classProperty = { + componentType: MetadataComponentType.UINT64, + normalized: false, + }; + const array = new BigUint64Array([12345678900n, 23456789000n]); + const dataView = new DataView( + array.buffer, + array.byteOffset, + array.byteLength, + ); + const dataViewOffset = 8; + const value = MetadataPicking.decodeRawMetadataValueComponent( + classProperty, + dataView, + dataViewOffset, + ); + expect(value).toBe(23456789000n); + }); + + it("decodes component with type normalized UINT64", function () { + const classProperty = { + componentType: MetadataComponentType.UINT64, + normalized: true, + }; + const array = new BigUint64Array([12345678900n, 23456789000n]); + const dataView = new DataView( + array.buffer, + array.byteOffset, + array.byteLength, + ); + const dataViewOffset = 8; + const value = MetadataPicking.decodeRawMetadataValueComponent( + classProperty, + dataView, + dataViewOffset, + ); + expect(Number(value)).toBeCloseTo( + Number(23456789000n / 18446744073709551615n), + precision, + ); + }); + + it("decodes component with type FLOAT32", function () { + const classProperty = { + componentType: MetadataComponentType.FLOAT32, + normalized: false, + }; + const array = new Float32Array([1.23, 2.34]); + const dataView = new DataView( + array.buffer, + array.byteOffset, + array.byteLength, + ); + const dataViewOffset = 4; + const value = MetadataPicking.decodeRawMetadataValueComponent( + classProperty, + dataView, + dataViewOffset, + ); + expect(value).toBeCloseTo(2.34, precision); + }); + + it("decodes component with type FLOAT64", function () { + const classProperty = { + componentType: MetadataComponentType.FLOAT64, + normalized: false, + }; + const array = new Float64Array([1.23, 2.34]); + const dataView = new DataView( + array.buffer, + array.byteOffset, + array.byteLength, + ); + const dataViewOffset = 8; + const value = MetadataPicking.decodeRawMetadataValueComponent( + classProperty, + dataView, + dataViewOffset, + ); + expect(value).toBeCloseTo(2.34, precision); + }); + }); +}); From 8cb917ca48098abfe086d551f98d4cefe2d7c812 Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Tue, 29 Oct 2024 15:07:47 +0100 Subject: [PATCH 023/175] Fix typo in JSDoc --- packages/engine/Source/Scene/MetadataPicking.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/engine/Source/Scene/MetadataPicking.js b/packages/engine/Source/Scene/MetadataPicking.js index 5aa1259330f9..6f4e9c5a2d7b 100644 --- a/packages/engine/Source/Scene/MetadataPicking.js +++ b/packages/engine/Source/Scene/MetadataPicking.js @@ -272,7 +272,7 @@ MetadataPicking.convertToObjectType = function (type, value) { }; /** - * Converts the given type into an raw value or array representation. + * Converts the given type into a raw value or array representation. * * For `VECn/MATn` types, the given value is converted into an array. * For other types, the value is returned directly From cc3412d8370085c13cc52b88bb7d3afff3983b5d Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Tue, 29 Oct 2024 17:51:00 +0100 Subject: [PATCH 024/175] Add error handling and documentation --- .../engine/Source/Scene/MetadataPicking.js | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/packages/engine/Source/Scene/MetadataPicking.js b/packages/engine/Source/Scene/MetadataPicking.js index 6f4e9c5a2d7b..23eaea7c611c 100644 --- a/packages/engine/Source/Scene/MetadataPicking.js +++ b/packages/engine/Source/Scene/MetadataPicking.js @@ -82,7 +82,7 @@ MetadataPicking.decodeRawMetadataValue = function ( * @param {number} dataViewOffset The byte offset within the data view from * which the component should be read * @returns {number|bigint|undefined} The metadata value component - * @throws RuntimeError If the component o the given property is not + * @throws RuntimeError If the component of the given property is not * a valid `MetadataComponentType` * @throws RangeError If reading the data from the given data view would * cause an out-of-bounds access @@ -123,6 +123,13 @@ MetadataPicking.decodeRawMetadataValueComponent = function ( * @param {number} elementIndex The index of the element. This is the index * inside the array for array-typed properties, and 0 for non-array types. * @returns {number|number[]|bigint|bigint[]|undefined} The decoded metadata value element + * @throws DeveloperError If the type of the given property is not + * a valid `MetadataType` + * @throws RuntimeError If the component of the given property is not + * a valid `MetadataComponentType` + * @throws RangeError If reading the data from the given data view would + * cause an out-of-bounds access + * */ MetadataPicking.decodeRawMetadataValueElement = function ( classProperty, @@ -192,6 +199,8 @@ MetadataPicking.decodeRawMetadataValueElement = function ( * @param {MetadataClassProperty} classProperty The `MetadataClassProperty` * @param {Uint8Array} rawPixelValues The raw values * @returns {number|bigint|number[]|bigint[]|undefined} The value + * @throws RuntimeError If the class property has an invalid type + * or component type * * @private */ @@ -239,6 +248,7 @@ MetadataPicking.decodeRawMetadataValues = function ( * @param {string} type The `ClassProperty` type * @param {number|bigint|number[]|bigint[]|undefined} value The input value * @returns {any} The object representation + * @throws RuntimeError If the type is not a valid `MetadataType` */ MetadataPicking.convertToObjectType = function (type, value) { if (!defined(value)) { @@ -268,7 +278,7 @@ MetadataPicking.convertToObjectType = function (type, value) { return Matrix4.unpack(numbers, 0, new Matrix4()); } // Should never happen: - return value; + throw new RuntimeError(`Invalid metadata object type: ${type}`); }; /** @@ -280,6 +290,7 @@ MetadataPicking.convertToObjectType = function (type, value) { * @param {string} type The `ClassProperty` type * @param {any} value The input value * @returns {any} The array representation + * @throws RuntimeError If the type is not a valid `MetadataType` */ MetadataPicking.convertFromObjectType = function (type, value) { if (!defined(value)) { @@ -319,8 +330,14 @@ MetadataPicking.convertFromObjectType = function (type, value) { * types into object types like `CartesianN`. * * @param {MetadataClassProperty} classProperty The `MetadataClassProperty` + * @param {MetadataClassProperty} metadataProperty The + * `PropertyTextureProperty` or `PropertyAttributeProperty` * @param {Uint8Array} rawPixelValues The raw values * @returns {any} The value + * @throws RuntimeError If the class property has an invalid type + * or component type + * @throws RangeError If the given pixel values do not have sufficient + * size to contain the expected value type * * @private */ From 49bd80e6aea84b6f467e5fca9df6d9c52ebd66e5 Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Tue, 29 Oct 2024 17:51:18 +0100 Subject: [PATCH 025/175] More specs for MetadataPicking --- .../engine/Specs/Scene/MetadataPickingSpec.js | 949 +++++++++++++++++- 1 file changed, 929 insertions(+), 20 deletions(-) diff --git a/packages/engine/Specs/Scene/MetadataPickingSpec.js b/packages/engine/Specs/Scene/MetadataPickingSpec.js index e17bebb2677b..96c2f73986fb 100644 --- a/packages/engine/Specs/Scene/MetadataPickingSpec.js +++ b/packages/engine/Specs/Scene/MetadataPickingSpec.js @@ -1,6 +1,18 @@ -import { MetadataComponentType, MetadataPicking } from "../../index.js"; +import { + Cartesian2, + Cartesian3, + Cartesian4, + Matrix2, + Matrix3, + Matrix4, + MetadataComponentType, + MetadataPicking, + MetadataType, +} from "../../index.js"; -// The precision for Jasmine `toBeCloseTo` calls +// The precision for Jasmine `toBeCloseTo` calls. Note that this +// is not an "epsilon", but described as "The number of decimal +// points to check" ... const precision = 3; describe("Scene/MetadataPicking", function () { @@ -247,7 +259,7 @@ describe("Scene/MetadataPicking", function () { }).toThrowError(); }); - it("decodes component with type INT8", function () { + it("decodes component with componentType INT8", function () { const classProperty = { componentType: MetadataComponentType.INT8, normalized: false, @@ -267,7 +279,7 @@ describe("Scene/MetadataPicking", function () { expect(value).toBe(23); }); - it("decodes component with type normalized INT8", function () { + it("decodes component with componentType normalized INT8", function () { const classProperty = { componentType: MetadataComponentType.INT8, normalized: true, @@ -287,7 +299,7 @@ describe("Scene/MetadataPicking", function () { expect(value).toBeCloseTo(23 / 127.0, precision); }); - it("decodes component with type UINT8", function () { + it("decodes component with componentType UINT8", function () { const classProperty = { componentType: MetadataComponentType.UINT8, normalized: false, @@ -307,7 +319,7 @@ describe("Scene/MetadataPicking", function () { expect(value).toBe(23); }); - it("decodes component with type normalized UINT8", function () { + it("decodes component with componentType normalized UINT8", function () { const classProperty = { componentType: MetadataComponentType.UINT8, normalized: true, @@ -327,7 +339,7 @@ describe("Scene/MetadataPicking", function () { expect(value).toBeCloseTo(23 / 255.0, precision); }); - it("decodes component with type INT16", function () { + it("decodes component with componentType INT16", function () { const classProperty = { componentType: MetadataComponentType.INT16, normalized: false, @@ -347,7 +359,7 @@ describe("Scene/MetadataPicking", function () { expect(value).toBe(2345); }); - it("decodes component with type normalized INT16", function () { + it("decodes component with componentType normalized INT16", function () { const classProperty = { componentType: MetadataComponentType.INT16, normalized: true, @@ -367,7 +379,7 @@ describe("Scene/MetadataPicking", function () { expect(value).toBeCloseTo(2345 / 32767.0, precision); }); - it("decodes component with type UINT16", function () { + it("decodes component with componentType UINT16", function () { const classProperty = { componentType: MetadataComponentType.UINT16, normalized: false, @@ -387,7 +399,7 @@ describe("Scene/MetadataPicking", function () { expect(value).toBe(2345); }); - it("decodes component with type normalized UINT16", function () { + it("decodes component with componentType normalized UINT16", function () { const classProperty = { componentType: MetadataComponentType.UINT16, normalized: true, @@ -407,7 +419,7 @@ describe("Scene/MetadataPicking", function () { expect(value).toBeCloseTo(2345 / 65535.0, precision); }); - it("decodes component with type INT32", function () { + it("decodes component with componentType INT32", function () { const classProperty = { componentType: MetadataComponentType.INT32, normalized: false, @@ -427,7 +439,7 @@ describe("Scene/MetadataPicking", function () { expect(value).toBe(234567); }); - it("decodes component with type normalized INT32", function () { + it("decodes component with componentType normalized INT32", function () { const classProperty = { componentType: MetadataComponentType.INT32, normalized: true, @@ -447,7 +459,7 @@ describe("Scene/MetadataPicking", function () { expect(value).toBeCloseTo(234567 / 2147483647.0, precision); }); - it("decodes component with type UINT32", function () { + it("decodes component with componentType UINT32", function () { const classProperty = { componentType: MetadataComponentType.UINT32, normalized: false, @@ -467,7 +479,7 @@ describe("Scene/MetadataPicking", function () { expect(value).toBe(234567); }); - it("decodes component with type normalized UINT32", function () { + it("decodes component with componentType normalized UINT32", function () { const classProperty = { componentType: MetadataComponentType.UINT32, normalized: true, @@ -487,7 +499,7 @@ describe("Scene/MetadataPicking", function () { expect(value).toBeCloseTo(234567 / 4294967295.0, precision); }); - it("decodes component with type INT64", function () { + it("decodes component with componentType INT64", function () { const classProperty = { componentType: MetadataComponentType.INT64, normalized: false, @@ -507,7 +519,7 @@ describe("Scene/MetadataPicking", function () { expect(value).toBe(23456789000n); }); - it("decodes component with type normalized INT64", function () { + it("decodes component with componentType normalized INT64", function () { const classProperty = { componentType: MetadataComponentType.INT64, normalized: true, @@ -530,7 +542,7 @@ describe("Scene/MetadataPicking", function () { ); }); - it("decodes component with type UINT64", function () { + it("decodes component with componentType UINT64", function () { const classProperty = { componentType: MetadataComponentType.UINT64, normalized: false, @@ -550,7 +562,7 @@ describe("Scene/MetadataPicking", function () { expect(value).toBe(23456789000n); }); - it("decodes component with type normalized UINT64", function () { + it("decodes component with componentType normalized UINT64", function () { const classProperty = { componentType: MetadataComponentType.UINT64, normalized: true, @@ -573,7 +585,7 @@ describe("Scene/MetadataPicking", function () { ); }); - it("decodes component with type FLOAT32", function () { + it("decodes component with componentType FLOAT32", function () { const classProperty = { componentType: MetadataComponentType.FLOAT32, normalized: false, @@ -593,7 +605,7 @@ describe("Scene/MetadataPicking", function () { expect(value).toBeCloseTo(2.34, precision); }); - it("decodes component with type FLOAT64", function () { + it("decodes component with componentType FLOAT64", function () { const classProperty = { componentType: MetadataComponentType.FLOAT64, normalized: false, @@ -613,4 +625,901 @@ describe("Scene/MetadataPicking", function () { expect(value).toBeCloseTo(2.34, precision); }); }); + + describe("decodeRawMetadataValueElement", function () { + it("throws for invalid component type", function () { + expect(function () { + const classProperty = { + type: "SCALAR", + componentType: "NOT_A_COMPONENT_TYPE", + normalized: false, + }; + const array = new Int8Array([12, 23]); + const dataView = new DataView( + array.buffer, + array.byteOffset, + array.byteLength, + ); + const elementIndex = 0; + MetadataPicking.decodeRawMetadataValueElement( + classProperty, + dataView, + elementIndex, + ); + }).toThrowError(); + }); + + it("throws for invalid type", function () { + expect(function () { + const classProperty = { + type: "NOT_A_TYPE", + componentType: MetadataComponentType.INT8, + normalized: false, + }; + const array = new Int8Array([12, 23]); + const dataView = new DataView( + array.buffer, + array.byteOffset, + array.byteLength, + ); + const elementIndex = 0; + MetadataPicking.decodeRawMetadataValueElement( + classProperty, + dataView, + elementIndex, + ); + }).toThrowError(); + }); + + it("throws for invalid element index", function () { + expect(function () { + const classProperty = { + type: "SCALAR", + componentType: MetadataComponentType.INT8, + normalized: false, + }; + const array = new Int8Array([12, 23]); + const dataView = new DataView( + array.buffer, + array.byteOffset, + array.byteLength, + ); + const elementIndex = 1234; + MetadataPicking.decodeRawMetadataValueElement( + classProperty, + dataView, + elementIndex, + ); + }).toThrowError(); + }); + + it("decodes element with type SCALAR and component type INT8", function () { + const classProperty = { + type: MetadataType.SCALAR, + componentType: MetadataComponentType.INT8, + normalized: false, + }; + const array = new Int8Array([0, 1]); + const dataView = new DataView( + array.buffer, + array.byteOffset, + array.byteLength, + ); + const elementIndex = 1; + const value = MetadataPicking.decodeRawMetadataValueElement( + classProperty, + dataView, + elementIndex, + ); + expect(value).toBe(1); + }); + + it("decodes element with type VEC2 and component type INT8", function () { + const classProperty = { + type: MetadataType.VEC2, + componentType: MetadataComponentType.INT8, + normalized: false, + }; + const array = new Int8Array([0, 1, 2, 3]); + const dataView = new DataView( + array.buffer, + array.byteOffset, + array.byteLength, + ); + const elementIndex = 1; + const value = MetadataPicking.decodeRawMetadataValueElement( + classProperty, + dataView, + elementIndex, + ); + expect(value).toEqual([2, 3]); + }); + + it("decodes element with type VEC3 and component type INT8", function () { + const classProperty = { + type: MetadataType.VEC3, + componentType: MetadataComponentType.INT8, + normalized: false, + }; + const array = new Int8Array([0, 1, 2, 3, 4, 5]); + const dataView = new DataView( + array.buffer, + array.byteOffset, + array.byteLength, + ); + const elementIndex = 1; + const value = MetadataPicking.decodeRawMetadataValueElement( + classProperty, + dataView, + elementIndex, + ); + expect(value).toEqual([3, 4, 5]); + }); + + it("decodes element with type VEC4 and component type INT8", function () { + const classProperty = { + type: MetadataType.VEC4, + componentType: MetadataComponentType.INT8, + normalized: false, + }; + const array = new Int8Array([0, 1, 2, 3, 4, 5, 6, 7]); + const dataView = new DataView( + array.buffer, + array.byteOffset, + array.byteLength, + ); + const elementIndex = 1; + const value = MetadataPicking.decodeRawMetadataValueElement( + classProperty, + dataView, + elementIndex, + ); + expect(value).toEqual([4, 5, 6, 7]); + }); + + it("decodes element with type MAT2 and component type INT8", function () { + const classProperty = { + type: MetadataType.MAT2, + componentType: MetadataComponentType.INT8, + normalized: false, + }; + const array = new Int8Array([0, 1, 2, 3, 4, 5, 6, 7]); + const dataView = new DataView( + array.buffer, + array.byteOffset, + array.byteLength, + ); + const elementIndex = 1; + const value = MetadataPicking.decodeRawMetadataValueElement( + classProperty, + dataView, + elementIndex, + ); + expect(value).toEqual([4, 5, 6, 7]); + }); + + it("decodes element with type MAT3 and component type INT8", function () { + const classProperty = { + type: MetadataType.MAT3, + componentType: MetadataComponentType.INT8, + normalized: false, + }; + const array = new Int8Array([ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, + ]); + const dataView = new DataView( + array.buffer, + array.byteOffset, + array.byteLength, + ); + const elementIndex = 1; + const value = MetadataPicking.decodeRawMetadataValueElement( + classProperty, + dataView, + elementIndex, + ); + expect(value).toEqual([9, 10, 11, 12, 13, 14, 15, 16, 17]); + }); + + it("decodes element with type MAT4 and component type INT8", function () { + const classProperty = { + type: MetadataType.MAT4, + componentType: MetadataComponentType.INT8, + normalized: false, + }; + const array = new Int8Array([ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + ]); + const dataView = new DataView( + array.buffer, + array.byteOffset, + array.byteLength, + ); + const elementIndex = 1; + const value = MetadataPicking.decodeRawMetadataValueElement( + classProperty, + dataView, + elementIndex, + ); + expect(value).toEqual([ + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + ]); + }); + + it("decodes element with type SCALAR and component type FLOAT32", function () { + const classProperty = { + type: MetadataType.SCALAR, + componentType: MetadataComponentType.FLOAT32, + normalized: false, + }; + const array = new Float32Array([0, 1]); + const dataView = new DataView( + array.buffer, + array.byteOffset, + array.byteLength, + ); + const elementIndex = 1; + const value = MetadataPicking.decodeRawMetadataValueElement( + classProperty, + dataView, + elementIndex, + ); + expect(value).toBe(1); + }); + + it("decodes element with type VEC2 and component type FLOAT32", function () { + const classProperty = { + type: MetadataType.VEC2, + componentType: MetadataComponentType.FLOAT32, + normalized: false, + }; + const array = new Float32Array([0, 1, 2, 3]); + const dataView = new DataView( + array.buffer, + array.byteOffset, + array.byteLength, + ); + const elementIndex = 1; + const value = MetadataPicking.decodeRawMetadataValueElement( + classProperty, + dataView, + elementIndex, + ); + expect(value).toEqual([2, 3]); + }); + + it("decodes element with type VEC3 and component type FLOAT32", function () { + const classProperty = { + type: MetadataType.VEC3, + componentType: MetadataComponentType.FLOAT32, + normalized: false, + }; + const array = new Float32Array([0, 1, 2, 3, 4, 5]); + const dataView = new DataView( + array.buffer, + array.byteOffset, + array.byteLength, + ); + const elementIndex = 1; + const value = MetadataPicking.decodeRawMetadataValueElement( + classProperty, + dataView, + elementIndex, + ); + expect(value).toEqual([3, 4, 5]); + }); + + it("decodes element with type VEC4 and component type FLOAT32", function () { + const classProperty = { + type: MetadataType.VEC4, + componentType: MetadataComponentType.FLOAT32, + normalized: false, + }; + const array = new Float32Array([0, 1, 2, 3, 4, 5, 6, 7]); + const dataView = new DataView( + array.buffer, + array.byteOffset, + array.byteLength, + ); + const elementIndex = 1; + const value = MetadataPicking.decodeRawMetadataValueElement( + classProperty, + dataView, + elementIndex, + ); + expect(value).toEqual([4, 5, 6, 7]); + }); + + it("decodes element with type MAT2 and component type FLOAT32", function () { + const classProperty = { + type: MetadataType.MAT2, + componentType: MetadataComponentType.FLOAT32, + normalized: false, + }; + const array = new Float32Array([0, 1, 2, 3, 4, 5, 6, 7]); + const dataView = new DataView( + array.buffer, + array.byteOffset, + array.byteLength, + ); + const elementIndex = 1; + const value = MetadataPicking.decodeRawMetadataValueElement( + classProperty, + dataView, + elementIndex, + ); + expect(value).toEqual([4, 5, 6, 7]); + }); + + it("decodes element with type MAT3 and component type FLOAT32", function () { + const classProperty = { + type: MetadataType.MAT3, + componentType: MetadataComponentType.FLOAT32, + normalized: false, + }; + const array = new Float32Array([ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, + ]); + const dataView = new DataView( + array.buffer, + array.byteOffset, + array.byteLength, + ); + const elementIndex = 1; + const value = MetadataPicking.decodeRawMetadataValueElement( + classProperty, + dataView, + elementIndex, + ); + expect(value).toEqual([9, 10, 11, 12, 13, 14, 15, 16, 17]); + }); + + it("decodes element with type MAT4 and component type FLOAT32", function () { + const classProperty = { + type: MetadataType.MAT4, + componentType: MetadataComponentType.FLOAT32, + normalized: false, + }; + const array = new Float32Array([ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + ]); + const dataView = new DataView( + array.buffer, + array.byteOffset, + array.byteLength, + ); + const elementIndex = 1; + const value = MetadataPicking.decodeRawMetadataValueElement( + classProperty, + dataView, + elementIndex, + ); + expect(value).toEqual([ + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + ]); + }); + }); + + describe("decodeRawMetadataValues", function () { + it("throws for invalid component type", function () { + expect(function () { + const classProperty = { + type: "SCALAR", + componentType: "NOT_A_COMPONENT_TYPE", + normalized: false, + }; + const rawPixelValues = new Uint8Array([0, 1, 2, 3]); + MetadataPicking.decodeRawMetadataValues(classProperty, rawPixelValues); + }).toThrowError(); + }); + it("throws for invalid type", function () { + expect(function () { + const classProperty = { + type: "NOT_A_TYPE", + componentType: MetadataComponentType.UINT8, + normalized: false, + }; + const rawPixelValues = new Uint8Array([0, 1, 2, 3]); + MetadataPicking.decodeRawMetadataValues(classProperty, rawPixelValues); + }).toThrowError(); + }); + + it("throws for invalid input length", function () { + expect(function () { + const classProperty = { + type: MetadataType.VEC2, + componentType: MetadataComponentType.UINT8, + normalized: false, + }; + const rawPixelValues = new Uint8Array([0]); + MetadataPicking.decodeRawMetadataValues(classProperty, rawPixelValues); + }).toThrowError(); + }); + + it("decodes raw values with type SCALAR and component type INT8", function () { + const classProperty = { + type: MetadataType.SCALAR, + componentType: MetadataComponentType.INT8, + normalized: false, + }; + const rawPixelValues = new Uint8Array([12, 23, 34, 45]); + const value = MetadataPicking.decodeRawMetadataValues( + classProperty, + rawPixelValues, + ); + expect(value).toEqual(12); + }); + + it("decodes raw values with type VEC2 and component type INT8", function () { + const classProperty = { + type: MetadataType.VEC2, + componentType: MetadataComponentType.INT8, + normalized: false, + }; + const rawPixelValues = new Uint8Array([0, 1, 2, 3]); + const value = MetadataPicking.decodeRawMetadataValues( + classProperty, + rawPixelValues, + ); + expect(value).toEqual([0, 1]); + }); + + it("decodes raw values with type VEC3 and component type INT8", function () { + const classProperty = { + type: MetadataType.VEC3, + componentType: MetadataComponentType.INT8, + normalized: false, + }; + const rawPixelValues = new Uint8Array([0, 1, 2, 3]); + const value = MetadataPicking.decodeRawMetadataValues( + classProperty, + rawPixelValues, + ); + expect(value).toEqual([0, 1, 2]); + }); + + it("decodes raw values with type VEC4 and component type INT8", function () { + const classProperty = { + type: MetadataType.VEC4, + componentType: MetadataComponentType.INT8, + normalized: false, + }; + const rawPixelValues = new Uint8Array([0, 1, 2, 3]); + const value = MetadataPicking.decodeRawMetadataValues( + classProperty, + rawPixelValues, + ); + expect(value).toEqual([0, 1, 2, 3]); + }); + }); + + describe("convertToObjectType", function () { + it("throws for invalid type", function () { + expect(function () { + const type = "NOT_A_TYPE"; + const value = 0; + MetadataPicking.convertToObjectType(type, value); + }).toThrowError(); + }); + + it("returns SCALAR, STRING, BOOLEAN, and ENUM types (and arrays thereof) unmodified", function () { + const value0 = MetadataPicking.convertToObjectType( + MetadataType.SCALAR, + 123, + ); + const value1 = MetadataPicking.convertToObjectType( + MetadataType.STRING, + "Value", + ); + const value2 = MetadataPicking.convertToObjectType( + MetadataType.SCALAR, + true, + ); + const value3 = MetadataPicking.convertToObjectType( + MetadataType.ENUM, + "VALUE", + ); + + expect(value0).toEqual(123); + expect(value1).toEqual("Value"); + expect(value2).toEqual(true); + expect(value3).toEqual("VALUE"); + + const value0a = MetadataPicking.convertToObjectType( + MetadataType.SCALAR, + [123, 234], + ); + const value1a = MetadataPicking.convertToObjectType(MetadataType.STRING, [ + "Value0", + "Value1", + ]); + const value2a = MetadataPicking.convertToObjectType(MetadataType.SCALAR, [ + true, + false, + ]); + const value3a = MetadataPicking.convertToObjectType(MetadataType.ENUM, [ + "VALUE_A", + "VALUE_B", + ]); + + expect(value0a).toEqual([123, 234]); + expect(value1a).toEqual(["Value0", "Value1"]); + expect(value2a).toEqual([true, false]); + expect(value3a).toEqual(["VALUE_A", "VALUE_B"]); + }); + + it("converts array-based VEC2 input into a Cartesian2", function () { + const array = [0, 1]; + const expected = Cartesian2.unpack(array, 0, new Cartesian2()); + const actual = MetadataPicking.convertToObjectType( + MetadataType.VEC2, + array, + ); + expect(actual).toEqual(expected); + }); + + it("converts array-based VEC3 input into a Cartesian3", function () { + const array = [0, 1, 2]; + const expected = Cartesian3.unpack(array, 0, new Cartesian3()); + const actual = MetadataPicking.convertToObjectType( + MetadataType.VEC3, + array, + ); + expect(actual).toEqual(expected); + }); + + it("converts array-based VEC4 input into a Cartesian4", function () { + const array = [0, 1, 2, 3]; + const expected = Cartesian4.unpack(array, 0, new Cartesian4()); + const actual = MetadataPicking.convertToObjectType( + MetadataType.VEC4, + array, + ); + expect(actual).toEqual(expected); + }); + + it("converts array-based MAT2 input into a Matrix2", function () { + const array = [0, 1, 2, 3]; + const expected = Matrix2.unpack(array, 0, new Matrix2()); + const actual = MetadataPicking.convertToObjectType( + MetadataType.MAT2, + array, + ); + expect(actual).toEqual(expected); + }); + + it("converts array-based MAT3 input into a Matrix3", function () { + const array = [0, 1, 2, 3, 4, 5, 6, 7, 8]; + const expected = Matrix3.unpack(array, 0, new Matrix3()); + const actual = MetadataPicking.convertToObjectType( + MetadataType.MAT3, + array, + ); + expect(actual).toEqual(expected); + }); + + it("converts array-based MAT4 input into a Matrix4", function () { + const array = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; + const expected = Matrix4.unpack(array, 0, new Matrix4()); + const actual = MetadataPicking.convertToObjectType( + MetadataType.MAT4, + array, + ); + expect(actual).toEqual(expected); + }); + }); + + describe("convertFromObjectType", function () { + it("throws for invalid type", function () { + expect(function () { + const type = "NOT_A_TYPE"; + const value = new Cartesian2(); + MetadataPicking.convertFromObjectType(type, value); + }).toThrowError(); + }); + + it("returns SCALAR, STRING, BOOLEAN, and ENUM types (and arrays thereof) unmodified", function () { + const value0 = MetadataPicking.convertFromObjectType( + MetadataType.SCALAR, + 123, + ); + const value1 = MetadataPicking.convertFromObjectType( + MetadataType.STRING, + "Value", + ); + const value2 = MetadataPicking.convertFromObjectType( + MetadataType.SCALAR, + true, + ); + const value3 = MetadataPicking.convertFromObjectType( + MetadataType.ENUM, + "VALUE", + ); + + expect(value0).toEqual(123); + expect(value1).toEqual("Value"); + expect(value2).toEqual(true); + expect(value3).toEqual("VALUE"); + + const value0a = MetadataPicking.convertFromObjectType( + MetadataType.SCALAR, + [123, 234], + ); + const value1a = MetadataPicking.convertFromObjectType( + MetadataType.STRING, + ["Value0", "Value1"], + ); + const value2a = MetadataPicking.convertFromObjectType( + MetadataType.SCALAR, + [true, false], + ); + const value3a = MetadataPicking.convertFromObjectType(MetadataType.ENUM, [ + "VALUE_A", + "VALUE_B", + ]); + + expect(value0a).toEqual([123, 234]); + expect(value1a).toEqual(["Value0", "Value1"]); + expect(value2a).toEqual([true, false]); + expect(value3a).toEqual(["VALUE_A", "VALUE_B"]); + }); + + it("converts Cartesian2 into array-based VEC2", function () { + const expected = [0, 1]; + const value = Cartesian2.unpack(expected, 0, new Cartesian2()); + const actual = MetadataPicking.convertFromObjectType( + MetadataType.VEC2, + value, + ); + expect(actual).toEqual(expected); + }); + + it("converts Cartesian3 into array-based VEC3", function () { + const expected = [0, 1, 2]; + const value = Cartesian3.unpack(expected, 0, new Cartesian3()); + const actual = MetadataPicking.convertFromObjectType( + MetadataType.VEC3, + value, + ); + expect(actual).toEqual(expected); + }); + + it("converts Cartesian4 into array-based VEC4", function () { + const expected = [0, 1, 2, 3]; + const value = Cartesian4.unpack(expected, 0, new Cartesian4()); + const actual = MetadataPicking.convertFromObjectType( + MetadataType.VEC4, + value, + ); + expect(actual).toEqual(expected); + }); + + it("converts Matrix2 into array-based MAT2", function () { + const expected = [0, 1, 2, 3]; + const value = Matrix2.unpack(expected, 0, new Matrix2()); + const actual = MetadataPicking.convertFromObjectType( + MetadataType.MAT2, + value, + ); + expect(actual).toEqual(expected); + }); + + it("converts Matrix3 into array-based MAT3", function () { + const expected = [0, 1, 2, 3, 4, 5, 6, 7, 8]; + const value = Matrix3.unpack(expected, 0, new Matrix3()); + const actual = MetadataPicking.convertFromObjectType( + MetadataType.MAT3, + value, + ); + expect(actual).toEqual(expected); + }); + + it("converts Matrix4 into array-based MAT4", function () { + const expected = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; + const value = Matrix4.unpack(expected, 0, new Matrix4()); + const actual = MetadataPicking.convertFromObjectType( + MetadataType.MAT4, + value, + ); + expect(actual).toEqual(expected); + }); + }); + + describe("decodeMetadataValues", function () { + it("throws for invalid type", function () { + expect(function () { + const classProperty = { + type: "NOT_A_TYPE", + componentType: MetadataComponentType.UINT8, + normalized: false, + }; + const metadataProperty = { + hasValueTransform: false, + }; + const rawPixelValues = new Uint8Array([0, 1, 2, 3]); + MetadataPicking.decodeMetadataValues( + classProperty, + metadataProperty, + rawPixelValues, + ); + }).toThrowError(); + }); + + it("decodes values with type SCALAR and component type INT8", function () { + const classProperty = { + type: MetadataType.SCALAR, + componentType: MetadataComponentType.INT8, + normalized: false, + }; + const metadataProperty = { + hasValueTransform: false, + }; + const rawPixelValues = new Uint8Array([12, 23, 34, 45]); + const value = MetadataPicking.decodeMetadataValues( + classProperty, + metadataProperty, + rawPixelValues, + ); + expect(value).toEqual(12); + }); + + it("decodes values with type VEC2 and component type INT8", function () { + const classProperty = { + type: MetadataType.VEC2, + componentType: MetadataComponentType.INT8, + normalized: false, + }; + const metadataProperty = { + hasValueTransform: false, + }; + const rawPixelValues = new Uint8Array([12, 23, 34, 45]); + const value = MetadataPicking.decodeMetadataValues( + classProperty, + metadataProperty, + rawPixelValues, + ); + expect(value).toEqual(new Cartesian2(12, 23)); + }); + + it("decodes values with type VEC3 and component type INT8", function () { + const classProperty = { + type: MetadataType.VEC3, + componentType: MetadataComponentType.INT8, + normalized: false, + }; + const metadataProperty = { + hasValueTransform: false, + }; + const rawPixelValues = new Uint8Array([12, 23, 34, 45]); + const value = MetadataPicking.decodeMetadataValues( + classProperty, + metadataProperty, + rawPixelValues, + ); + expect(value).toEqual(new Cartesian3(12, 23, 34)); + }); + + it("decodes values with type VEC4 and component type INT8", function () { + const classProperty = { + type: MetadataType.VEC4, + componentType: MetadataComponentType.INT8, + normalized: false, + }; + const metadataProperty = { + hasValueTransform: false, + }; + const rawPixelValues = new Uint8Array([12, 23, 34, 45]); + const value = MetadataPicking.decodeMetadataValues( + classProperty, + metadataProperty, + rawPixelValues, + ); + expect(value).toEqual(new Cartesian4(12, 23, 34, 45)); + }); + + it("decodes values with type SCALAR and component type INT8 and offset and scale", function () { + const classProperty = { + type: MetadataType.SCALAR, + componentType: MetadataComponentType.INT8, + normalized: false, + }; + const offset = 100; + const scale = 2; + const metadataProperty = { + hasValueTransform: true, + offset: offset, + scale: scale, + }; + const rawPixelValues = new Uint8Array([12, 23, 34, 45]); + const value = MetadataPicking.decodeMetadataValues( + classProperty, + metadataProperty, + rawPixelValues, + ); + expect(value).toEqual(offset + 12 * scale); + }); + + it("decodes values with type VEC2 and component type INT8 and offset and scale", function () { + const classProperty = { + type: MetadataType.VEC2, + componentType: MetadataComponentType.INT8, + normalized: false, + }; + const offset = new Cartesian2(100, 200); + const scale = new Cartesian2(2, 3); + const metadataProperty = { + hasValueTransform: true, + offset: offset, + scale: scale, + }; + const rawPixelValues = new Uint8Array([12, 23, 34, 45]); + const value = MetadataPicking.decodeMetadataValues( + classProperty, + metadataProperty, + rawPixelValues, + ); + const expected = new Cartesian2( + offset.x + 12 * scale.x, + offset.y + 23 * scale.y, + ); + expect(value).toEqual(expected); + }); + + it("decodes values with type VEC3 and component type INT8 and offset and scale", function () { + const classProperty = { + type: MetadataType.VEC3, + componentType: MetadataComponentType.INT8, + normalized: false, + }; + const offset = new Cartesian3(100, 200, 300); + const scale = new Cartesian3(2, 3, 4); + const metadataProperty = { + hasValueTransform: true, + offset: offset, + scale: scale, + }; + const rawPixelValues = new Uint8Array([12, 23, 34, 45]); + const value = MetadataPicking.decodeMetadataValues( + classProperty, + metadataProperty, + rawPixelValues, + ); + const expected = new Cartesian3( + offset.x + 12 * scale.x, + offset.y + 23 * scale.y, + offset.z + 34 * scale.z, + ); + expect(value).toEqual(expected); + }); + + it("decodes values with type VEC4 and component type INT8 and offset and scale", function () { + const classProperty = { + type: MetadataType.VEC4, + componentType: MetadataComponentType.INT8, + normalized: false, + }; + const offset = new Cartesian4(100, 200, 300, 400); + const scale = new Cartesian4(2, 3, 4, 5); + const metadataProperty = { + hasValueTransform: true, + offset: offset, + scale: scale, + }; + const rawPixelValues = new Uint8Array([12, 23, 34, 45]); + const value = MetadataPicking.decodeMetadataValues( + classProperty, + metadataProperty, + rawPixelValues, + ); + const expected = new Cartesian4( + offset.x + 12 * scale.x, + offset.y + 23 * scale.y, + offset.z + 34 * scale.z, + offset.w + 45 * scale.w, + ); + expect(value).toEqual(expected); + }); + }); }); From 76a814968044ed6160b7093254b95271f7e9cd57 Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Tue, 29 Oct 2024 18:01:14 +0100 Subject: [PATCH 026/175] Removed specs for errors that are not thrown --- .../engine/Source/Scene/MetadataPicking.js | 5 +-- .../engine/Specs/Scene/MetadataPickingSpec.js | 33 ------------------- 2 files changed, 1 insertion(+), 37 deletions(-) diff --git a/packages/engine/Source/Scene/MetadataPicking.js b/packages/engine/Source/Scene/MetadataPicking.js index 23eaea7c611c..5364824c64bd 100644 --- a/packages/engine/Source/Scene/MetadataPicking.js +++ b/packages/engine/Source/Scene/MetadataPicking.js @@ -123,8 +123,6 @@ MetadataPicking.decodeRawMetadataValueComponent = function ( * @param {number} elementIndex The index of the element. This is the index * inside the array for array-typed properties, and 0 for non-array types. * @returns {number|number[]|bigint|bigint[]|undefined} The decoded metadata value element - * @throws DeveloperError If the type of the given property is not - * a valid `MetadataType` * @throws RuntimeError If the component of the given property is not * a valid `MetadataComponentType` * @throws RangeError If reading the data from the given data view would @@ -199,8 +197,7 @@ MetadataPicking.decodeRawMetadataValueElement = function ( * @param {MetadataClassProperty} classProperty The `MetadataClassProperty` * @param {Uint8Array} rawPixelValues The raw values * @returns {number|bigint|number[]|bigint[]|undefined} The value - * @throws RuntimeError If the class property has an invalid type - * or component type + * @throws RuntimeError If the class property has an invalid component type * * @private */ diff --git a/packages/engine/Specs/Scene/MetadataPickingSpec.js b/packages/engine/Specs/Scene/MetadataPickingSpec.js index 96c2f73986fb..5f1cb8bd5a72 100644 --- a/packages/engine/Specs/Scene/MetadataPickingSpec.js +++ b/packages/engine/Specs/Scene/MetadataPickingSpec.js @@ -649,28 +649,6 @@ describe("Scene/MetadataPicking", function () { }).toThrowError(); }); - it("throws for invalid type", function () { - expect(function () { - const classProperty = { - type: "NOT_A_TYPE", - componentType: MetadataComponentType.INT8, - normalized: false, - }; - const array = new Int8Array([12, 23]); - const dataView = new DataView( - array.buffer, - array.byteOffset, - array.byteLength, - ); - const elementIndex = 0; - MetadataPicking.decodeRawMetadataValueElement( - classProperty, - dataView, - elementIndex, - ); - }).toThrowError(); - }); - it("throws for invalid element index", function () { expect(function () { const classProperty = { @@ -1014,17 +992,6 @@ describe("Scene/MetadataPicking", function () { MetadataPicking.decodeRawMetadataValues(classProperty, rawPixelValues); }).toThrowError(); }); - it("throws for invalid type", function () { - expect(function () { - const classProperty = { - type: "NOT_A_TYPE", - componentType: MetadataComponentType.UINT8, - normalized: false, - }; - const rawPixelValues = new Uint8Array([0, 1, 2, 3]); - MetadataPicking.decodeRawMetadataValues(classProperty, rawPixelValues); - }).toThrowError(); - }); it("throws for invalid input length", function () { expect(function () { From fe5da329d385df33a11928150e7b8529f3dacfcb Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Wed, 30 Oct 2024 01:19:16 +0100 Subject: [PATCH 027/175] Updated JSDoc --- .../engine/Source/Scene/PickedMetadataInfo.js | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/packages/engine/Source/Scene/PickedMetadataInfo.js b/packages/engine/Source/Scene/PickedMetadataInfo.js index 8a80e3842e1e..40a727a8de43 100644 --- a/packages/engine/Source/Scene/PickedMetadataInfo.js +++ b/packages/engine/Source/Scene/PickedMetadataInfo.js @@ -45,19 +45,11 @@ function PickedMetadataInfo( this.classProperty = classProperty; /** - * The the `PropertyTextureProperty` or `PropertyAttributeProperty` - * that is described by this structure, as obtained from the - * property texture or property attribute of the `StructuralMetadata` - * that matches the class name and property name. + * The metadata property that is described by this structure, as + * obtained from the property texture or property attribute of the + * `StructuralMetadata` that matches the class name and property name. * - * The `PropertyTextureProperty` and `PropertyAttributeProperty` - * types are not public and do not share a common base type. - * But they both define the getters - * - `hasValueTransform` (`boolean`) - * - `offset` (`any`) - * - `scale` (`any`) - * - * @type {object} + * @type {PropertyTextureProperty|PropertyAttributeProperty} */ this.metadataProperty = metadataProperty; } From efe7040f03b12d0f16b567151e3ecce990e71af8 Mon Sep 17 00:00:00 2001 From: Emanuele Mastaglia Date: Thu, 31 Oct 2024 13:16:57 +0100 Subject: [PATCH 028/175] fix: throw error when baseLayer is used with globe: false --- packages/widgets/Source/Viewer/Viewer.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packages/widgets/Source/Viewer/Viewer.js b/packages/widgets/Source/Viewer/Viewer.js index 3b6ec54ba847..29f1eaf2770a 100644 --- a/packages/widgets/Source/Viewer/Viewer.js +++ b/packages/widgets/Source/Viewer/Viewer.js @@ -504,6 +504,16 @@ Either specify options.terrainProvider instead or set options.baseLayerPicker to const scene = cesiumWidget.scene; + //>>includeStart('debug', pragmas.debug); + if ( + options.globe === false && + options.baseLayer !== undefined && + options.baseLayer !== false + ) { + throw new DeveloperError("Cannot use baseLayer when globe is disabled."); + } + //>>includeEnd('debug'); + const eventHelper = new EventHelper(); eventHelper.add(clock.onTick, Viewer.prototype._onTick, this); From 8ea80dbd6731d6b3eb7d6f07093b0958cf8569e8 Mon Sep 17 00:00:00 2001 From: Emanuele Mastaglia Date: Thu, 31 Oct 2024 13:17:33 +0100 Subject: [PATCH 029/175] test: add specs for baseLayer usage with globe: false --- packages/widgets/Specs/Viewer/ViewerSpec.js | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/packages/widgets/Specs/Viewer/ViewerSpec.js b/packages/widgets/Specs/Viewer/ViewerSpec.js index 49df43f46eb2..a967a34289e1 100644 --- a/packages/widgets/Specs/Viewer/ViewerSpec.js +++ b/packages/widgets/Specs/Viewer/ViewerSpec.js @@ -542,6 +542,23 @@ describe( ); }); + it("throws when baseLayer is provided with globe: false", function () { + expect(function () { + viewer = createViewer(container, { + globe: false, + baseLayer: new ImageryLayer(testProvider), + }); + }).toThrowDeveloperError(); + }); + + it("does not throw when globe is false and baseLayer is not provided", function () { + expect(function () { + viewer = createViewer(container, { + globe: false, + }); + }).not.toThrowDeveloperError(); + }); + it("can set baseLayer to false when BaseLayerPicker is disabled", function () { viewer = createViewer(container, { baseLayerPicker: false, From 3faeaecc0cafec12b073289472522e678473dd37 Mon Sep 17 00:00:00 2001 From: Emanuele Mastaglia Date: Thu, 31 Oct 2024 13:18:06 +0100 Subject: [PATCH 030/175] chore: add contributor name --- CONTRIBUTORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 5bd6a8a5cdaf..cc7913527093 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -415,3 +415,4 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for details on how to contribute to Cesiu - [Javier Sanchez](https://github.com/jvrjsanchez) - [Jérôme Fayot](https://github.com/jfayot) - [Kirn Kim](https://github.com/squrki) +- [Emanuele Mastaglia](https://github.com/Masty88) From f9675bc43fc60a3ccc2f7eb747cb78fac5b02284 Mon Sep 17 00:00:00 2001 From: Emanuele Mastaglia Date: Thu, 31 Oct 2024 13:23:12 +0100 Subject: [PATCH 031/175] docs: update CHANGES.md with baseLayer globe fix --- CHANGES.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 30178ac0d929..a1b2c2c48037 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -38,6 +38,8 @@ - Fix error with normalization of corner points for lines and corridors with collinear points. [#12255](https://github.com/CesiumGS/cesium/pull/12255) +- Added a `DeveloperError` when `globe` is set to `false` and a `baseLayer` is provided in `Viewer` options. This prevents errors caused by attempting to use a `baseLayer` without a globe.[#12274](https://github.com/CesiumGS/cesium/pull/12274) + ### 1.122 - 2024-10-01 #### @cesium/engine From 66dadfef3a1b8d878e8bf04db47233fde435d21b Mon Sep 17 00:00:00 2001 From: Emanuele Mastaglia Date: Thu, 31 Oct 2024 13:26:40 +0100 Subject: [PATCH 032/175] docs: update JSDoc to document baseLayer with globe disabled --- packages/widgets/Source/Viewer/Viewer.js | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/widgets/Source/Viewer/Viewer.js b/packages/widgets/Source/Viewer/Viewer.js index 29f1eaf2770a..fb8df0818d6d 100644 --- a/packages/widgets/Source/Viewer/Viewer.js +++ b/packages/widgets/Source/Viewer/Viewer.js @@ -295,7 +295,7 @@ function enableVRUI(viewer, enabled) { * @property {ProviderViewModel[]} [imageryProviderViewModels=createDefaultImageryProviderViewModels()] The array of ProviderViewModels to be selectable from the BaseLayerPicker. This value is only valid if `baseLayerPicker` is set to true. * @property {ProviderViewModel} [selectedTerrainProviderViewModel] The view model for the current base terrain layer, if not supplied the first available base layer is used. This value is only valid if `baseLayerPicker` is set to true. * @property {ProviderViewModel[]} [terrainProviderViewModels=createDefaultTerrainProviderViewModels()] The array of ProviderViewModels to be selectable from the BaseLayerPicker. This value is only valid if `baseLayerPicker` is set to true. - * @property {ImageryLayer|false} [baseLayer=ImageryLayer.fromWorldImagery()] The bottommost imagery layer applied to the globe. If set to false, no imagery provider will be added. This value is only valid if `baseLayerPicker` is set to false. + * @property {ImageryLayer|false} [baseLayer=ImageryLayer.fromWorldImagery()] The bottommost imagery layer applied to the globe. If set to false, no imagery provider will be added. This value is only valid if `baseLayerPicker` is set to false. Cannot be used when `globe` is set to false. * @property {Ellipsoid} [ellipsoid = Ellipsoid.default] The default ellipsoid. * @property {TerrainProvider} [terrainProvider=new EllipsoidTerrainProvider()] The terrain provider to use * @property {Terrain} [terrain] A terrain object which handles asynchronous terrain provider. Can only specify if options.terrainProvider is undefined. @@ -402,6 +402,16 @@ function Viewer(container, options) { container = getElement(container); options = defaultValue(options, defaultValue.EMPTY_OBJECT); + //>>includeStart('debug', pragmas.debug); + if ( + options.globe === false && + options.baseLayer !== undefined && + options.baseLayer !== false + ) { + throw new DeveloperError("Cannot use baseLayer when globe is disabled."); + } + //>>includeEnd('debug'); + const createBaseLayerPicker = (!defined(options.globe) || options.globe !== false) && (!defined(options.baseLayerPicker) || options.baseLayerPicker !== false); @@ -504,16 +514,6 @@ Either specify options.terrainProvider instead or set options.baseLayerPicker to const scene = cesiumWidget.scene; - //>>includeStart('debug', pragmas.debug); - if ( - options.globe === false && - options.baseLayer !== undefined && - options.baseLayer !== false - ) { - throw new DeveloperError("Cannot use baseLayer when globe is disabled."); - } - //>>includeEnd('debug'); - const eventHelper = new EventHelper(); eventHelper.add(clock.onTick, Viewer.prototype._onTick, this); From 7e16e2dc4c5b4b004bddd1ea28814dc59cc193c0 Mon Sep 17 00:00:00 2001 From: Emanuele Mastaglia Date: Thu, 31 Oct 2024 18:24:26 +0100 Subject: [PATCH 033/175] fix: Use defined() for options.baseLayer check when globe is disabled --- packages/widgets/Source/Viewer/Viewer.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/widgets/Source/Viewer/Viewer.js b/packages/widgets/Source/Viewer/Viewer.js index fb8df0818d6d..499a8d35ad07 100644 --- a/packages/widgets/Source/Viewer/Viewer.js +++ b/packages/widgets/Source/Viewer/Viewer.js @@ -405,12 +405,11 @@ function Viewer(container, options) { //>>includeStart('debug', pragmas.debug); if ( options.globe === false && - options.baseLayer !== undefined && + defined(options.baseLayer) && options.baseLayer !== false ) { throw new DeveloperError("Cannot use baseLayer when globe is disabled."); } - //>>includeEnd('debug'); const createBaseLayerPicker = (!defined(options.globe) || options.globe !== false) && From 0f6db77484718c2c706d4922a3118b02b40505f3 Mon Sep 17 00:00:00 2001 From: jjspace <8007967+jjspace@users.noreply.github.com> Date: Tue, 5 Nov 2024 10:14:56 -0500 Subject: [PATCH 034/175] initial rough api --- Apps/Sandcastle/gallery/iTwin Demo.html | 295 ++++++++++++++ packages/engine/Source/Core/ITwin.js | 66 ++++ .../Source/Scene/createIModel3DTileset.js | 371 ++++++++++++++++++ 3 files changed, 732 insertions(+) create mode 100644 Apps/Sandcastle/gallery/iTwin Demo.html create mode 100644 packages/engine/Source/Core/ITwin.js create mode 100644 packages/engine/Source/Scene/createIModel3DTileset.js diff --git a/Apps/Sandcastle/gallery/iTwin Demo.html b/Apps/Sandcastle/gallery/iTwin Demo.html new file mode 100644 index 000000000000..e1674a6b9e74 --- /dev/null +++ b/Apps/Sandcastle/gallery/iTwin Demo.html @@ -0,0 +1,295 @@ + + + + + + + + + Cesium Demo + + + + + +
+

Loading...

+
+
+ Initializing +
+ + + diff --git a/packages/engine/Source/Core/ITwin.js b/packages/engine/Source/Core/ITwin.js new file mode 100644 index 000000000000..23826cfc5d4b --- /dev/null +++ b/packages/engine/Source/Core/ITwin.js @@ -0,0 +1,66 @@ +import Resource from "./Resource.js"; + +/** + * Default settings for accessing the iTwin platform. + * + * Keys can be created using the iModels share routes {@link https://developer.bentley.com/apis/imodels-v2/operations/create-imodel-share/} + * + * An ion access token is only required if you are using any ion related APIs. + * A default access token is provided for evaluation purposes only. + * Sign up for a free ion account and get your own access token at {@link https://cesium.com} + * + * @see IonResource + * @see IonImageryProvider + * @see IonGeocoderService + * @see createWorldImagery + * @see createWorldTerrain + * @namespace ITwin + */ +const ITwin = {}; + +/** + * Gets or sets the default iTwin access token. + * + * TODO: I'm not sure we can even do this kind of access token. Each route seems to need it's own scopes + * and we may not be able to guarantee this "top level token" has them all + * So far we use + * `mesh-export:read` for loading meshes GET /mesh-export(s) + * `mesh-export:modify` if we want to include a function to create an export + * `itwin-platform` if we want to use the iModel shares ourselves GET /imodels/{id}/shares + * + * + * @type {string|undefined} + */ +ITwin.defaultAccessToken = undefined; + +/** + * Gets or sets the default Google Map Tiles API endpoint. + * + * @type {string|Resource} + * @default https://api.bentley.com + */ +ITwin.apiEndpoint = new Resource({ + url: "https://api.bentley.com", +}); + +// TODO: this should only be needed if we have a way to generate really long term access tokens +// to sample data that is accessible to everyone +// ITwin.getDefaultTokenCredit = function (providedKey) { +// if (providedKey !== defaultAccessToken) { +// return undefined; +// } + +// if (!defined(defaultTokenCredit)) { +// const defaultTokenMessage = +// ' \ +// This application is using Cesium\'s default ion access token. Please assign Cesium.Ion.defaultAccessToken \ +// with an access token from your ion account before making any Cesium API calls. \ +// You can sign up for a free ion account at
https://cesium.com.'; + +// defaultTokenCredit = new Credit(defaultTokenMessage, true); +// } + +// return defaultTokenCredit; +// }; + +export default ITwin; diff --git a/packages/engine/Source/Scene/createIModel3DTileset.js b/packages/engine/Source/Scene/createIModel3DTileset.js new file mode 100644 index 000000000000..1405aac4e5a2 --- /dev/null +++ b/packages/engine/Source/Scene/createIModel3DTileset.js @@ -0,0 +1,371 @@ +import Cesium3DTileset from "./Cesium3DTileset.js"; +import defined from "../Core/defined.js"; +import Resource from "../Core/Resource.js"; +import ITwin from "../Core/ITwin.js"; +import DeveloperError from "../Core/DeveloperError.js"; + +function delay(ms) { + return new Promise((res) => setTimeout(res, ms)); +} + +/** + * @enum {string} + */ +const ExportStatus = Object.freeze({ + NotStarted: "NotStarted", + InProgress: "InProgress", + Complete: "Complete", + Invalid: "Invalid", +}); + +/** + * Type of an export currently, only GLTF and 3DFT are documented + * The CESIUM option is what we were told to use with Sandcastle + * I've also seen the IMODEL one but don't know where it's from + * @enum {string} + */ +const ExportType = Object.freeze({ + "3DFT": "3DFT", + GLFT: "GLTF", + IMODEL: "IMODEL", + CESIUM: "CESIUM", +}); + +/** + * @typedef {Object} GeometryOptions + * @property {boolean} includeLines + * @property {number} chordTol + * @property {number} angleTol + * @property {number} decimationTol + * @property {number} maxEdgeLength + * @property {number} minBRepFeatureSize + * @property {number} minLineStyleComponentSize + */ + +/** + * @typedef {Object} ViewDefinitionFilter + * @property {string[]} models Array of included model IDs. + * @property {string[]} categories Array of included category IDs. + * @property {string[]} neverDrawn Array of element IDs to filter out. + */ + +/** + * @typedef {Object} StartExport + * @property {string} iModelId + * @property {string} changesetId + * @property {ExportType} exportType Type of mesh to create. Currently, only GLTF and 3DFT are supported and undocumented CESIUM option + * @property {GeometryOptions} geometryOptions + * @property {ViewDefinitionFilter} viewDefinitionFilter + */ + +/** + * @typedef {Object} Link + * @property {string} href + */ + +/** + * @typedef {Object} Export + * @property {string} id + * @property {string} displayName + * @property {ExportStatus} status + * @property {StartExport} request + * @property {{mesh: Link}} _links + */ + +/** + * @typedef {Object} ExportResponse + * @property {Export} export + */ + +/** + * Creates a {@link Cesium3DTileset} instance for the Google Photorealistic 3D Tiles tileset. + * + * @function + * + * @param {string} exportId + * @param {Cesium3DTileset.ConstructorOptions} [options] An object describing initialization options. + * @returns {Promise} + * + * @see ITwin + * + * @example + * // Use your own iTwin API key for mesh export + * Cesium.ITwin.defaultApiKey = "your-api-key"; + * + * const viewer = new Cesium.Viewer("cesiumContainer"); + * + * try { + * const tileset = await Cesium.createIModel3DTileset(); + * viewer.scene.primitives.add(tileset)); + * } catch (error) { + * console.log(`Error creating tileset: ${error}`); + * } + */ +async function createIModel3DTileset(exportId, options) { + if (!defined(ITwin.defaultAccessToken)) { + throw new DeveloperError("Must set ITwin.defaultAccessToken first"); + } + + options = options ?? {}; + + const timeoutAfter = 300000; + const start = Date.now(); + let result = await createIModel3DTileset.getExport(exportId); + let status = result.export.status; + + if (result.export.request.exportType !== ExportType.CESIUM) { + // This is an undocumented value but I think it's the only one we want to load + // TODO: should we even be checking this? + throw new Error(`Wrong export type ${result.export.request.exportType}`); + } + + // wait until the export is complete + while (status !== ExportStatus.Complete) { + await delay(5000); + result = await createIModel3DTileset.getExport(exportId); + status = result.export.status; + console.log(`Export is ${status}`); + + if (Date.now() - start > timeoutAfter) { + throw new Error("Export did not complete in time."); + } + } + + // This link is only valid 1 hour + let tilesetUrl = result.export._links.mesh.href; + const splitStr = tilesetUrl.split("?"); + // is there a cleaner way to do this? + tilesetUrl = `${splitStr[0]}/tileset.json?${splitStr[1]}`; + + const resource = new Resource({ + url: tilesetUrl, + }); + + return Cesium3DTileset.fromUrl(resource, options); +} + +/** + * @param {string} exportId + */ +createIModel3DTileset.getExport = async function (exportId) { + const headers = { + Authorization: ITwin.defaultAccessToken, + Accept: "application/vnd.bentley.itwin-platform.v1+json", + }; + + // obtain export for specified export id + const url = `${ITwin.apiEndpoint}mesh-export/${exportId}`; + + // TODO: this request is _really_ slow, like 7 whole second alone for me + // Arun said this was kinda normal but to keep track of the `x-correlation-id` of any that take EXTRA long + const response = await fetch(url, { headers }); + if (!response.ok) { + const result = await response.json(); + if (response.status === 401) { + throw new Error( + `Unauthorized, bad token, wrong scopes or headers bad. ${result.error.details[0].code}`, + ); + } else if (response.status === 404) { + throw new Error(`Requested export is not available ${exportId}`); + } else if (response.status === 429) { + throw new Error("Too many requests"); + } + throw new Error(`Unknown request failure ${response.status}`); + } + + /** @type {ExportResponse} */ + const result = await response.json(); + return result; +}; + +/** + * Get the list of exports for the given iModel + changeset + * + * @param {string} iModelId + * @param {string} changesetId + */ +createIModel3DTileset.getExports = async function (iModelId, changesetId) { + if (!defined(ITwin.defaultAccessToken)) { + throw new DeveloperError("Must set ITwin.defaultAccessToken first"); + } + const headers = { + Authorization: ITwin.defaultAccessToken, + Accept: "application/vnd.bentley.itwin-platform.v1+json", + Prefer: "return=representation", // or return=minimal (the default) + }; + + // obtain export for specified export id + let url = `${ITwin.apiEndpoint}mesh-export/?iModelId=${iModelId}`; + if (defined(changesetId) && changesetId !== "") { + url += `&changesetId=${changesetId}`; + } + + const response = await fetch(url, { headers }); + if (!response.ok) { + const result = await response.json(); + if (response.status === 401) { + throw new Error( + `Unauthorized, bad token, wrong scopes or headers bad. ${result.error.details[0].code}`, + ); + } else if (response.status === 422) { + throw new Error( + `Unprocessable Entity:${result.error.code} ${result.error.message}`, + ); + } else if (response.status === 429) { + throw new Error("Too many requests"); + } + throw new Error(`Unknown request failure ${response.status}`); + } + + /** @type {{exports: Export[]}} */ + const result = await response.json(); + return result; +}; + +/** + * Check the exports for the given iModel + changeset combination for any that + * have the desired CESIUM type and return that one + * + * @param {string} iModelId + * @param {string} changesetId + */ +createIModel3DTileset.checkForCesiumExport = async function ( + iModelId, + changesetId, +) { + const { exports } = await createIModel3DTileset.getExports( + iModelId, + changesetId, + ); + const cesiumExport = exports.find( + (e) => e.request.exportType === ExportType.CESIUM, + ); + return cesiumExport; +}; + +/** + * Start the export process for the given iModel + changeset. + * + * Pair this with the {@link checkForCesiumExport} function to avoid creating extra exports + * + * @example + * const cesiumExport = await Cesium.createIModel3DTileset.checkForCesiumExport(imodelId, changesetId); + * let exportId = cesiumExport?.id; + * if (!Cesium.defined(cesiumExport)) { + * exportId = await Cesium.createIModel3DTileset.createExportForModelId( + * imodelId, + * changesetId, + * accessToken, + * ); + * } + * + * @param {string} iModelId + * @param {string} changesetId + */ +createIModel3DTileset.createExportForModelId = async function ( + iModelId, + changesetId, +) { + if (!defined(ITwin.defaultAccessToken)) { + throw new DeveloperError("Must set ITwin.defaultAccessToken first"); + } + + console.log("Start Export"); + + changesetId = changesetId ?? ""; + + const requestOptions = { + method: "POST", + headers: { + Authorization: ITwin.defaultAccessToken, + Accept: "application/vnd.bentley.itwin-platform.v1+json", + "Content-Type": "application/json", + }, + body: JSON.stringify({ + iModelId, + changesetId, + exportType: "CESIUM", + }), + }; + + // initiate mesh export + const response = await fetch( + `https://api.bentley.com/mesh-export/`, + requestOptions, + ); + + if (!response.ok) { + const result = await response.json(); + if (response.status === 401) { + console.error("Unauthorized, bad token, wrong scopes or headers bad"); + console.error( + result.error.code, + result.error.message, + result.error.details, + ); + } else if (response.status === 403) { + console.error("Not allowed, forbidden"); + console.error(result.error.code, result.error.message); + } else if (response.status === 422) { + console.error("Unprocessable: Cannot create export job"); + console.error(result.error.code, result.error.message); + console.error(result.error.details); + } else if (response.status === 429) { + console.log( + "Too many requests, retry after:", + response.headers.get("retry-after"), + ); + console.error(result.error.code, result.error.message); + } else { + console.error("Bad request, unknown error", response); + } + return undefined; + } + + /** @type {ExportResponse} */ + const result = await response.json(); + return result.export.id; +}; + +/** + * Delete the specified export + * + * TODO: I'm not sure if we want this or not. Might belong better as an APP level function + * I just started creating helpers for all the routes under the `mesh-export` API + * for ease of access during testing + * + * @param {string} exportId + */ +createIModel3DTileset.deleteExport = async function (exportId) { + if (!defined(ITwin.defaultAccessToken)) { + throw new DeveloperError("Must set ITwin.defaultAccessToken first"); + } + const headers = { + Authorization: ITwin.defaultAccessToken, + Accept: "application/vnd.bentley.itwin-platform.v1+json", + }; + + // obtain export for specified export id + const url = `${ITwin.apiEndpoint}mesh-export/${exportId}`; + + const response = await fetch(url, { method: "DELETE", headers }); + if (!response.ok) { + const result = await response.json(); + if (response.status === 401) { + throw new Error( + `Unauthorized, bad token, wrong scopes or headers bad. ${result.error.details[0].code}`, + ); + } else if (response.status === 404) { + throw new Error("Export not found"); + } else if (response.status === 422) { + throw new Error( + `Unprocessable Entity:${result.error.code} ${result.error.message}`, + ); + } else if (response.status === 429) { + throw new Error("Too many requests"); + } + throw new Error(`Unknown request failure ${response.status}`); + } +}; + +export default createIModel3DTileset; From 421e5dda69820dd7e9a947f5644567325368d2d7 Mon Sep 17 00:00:00 2001 From: jjspace <8007967+jjspace@users.noreply.github.com> Date: Tue, 5 Nov 2024 16:33:11 -0500 Subject: [PATCH 035/175] restructure api, create tileset from model id --- Apps/Sandcastle/gallery/iTwin Demo.html | 158 ++------ packages/engine/Source/Core/ITwin.js | 274 +++++++++++++- .../Source/Scene/createIModel3DTileset.js | 358 +++--------------- 3 files changed, 351 insertions(+), 439 deletions(-) diff --git a/Apps/Sandcastle/gallery/iTwin Demo.html b/Apps/Sandcastle/gallery/iTwin Demo.html index e1674a6b9e74..d8d4865927e2 100644 --- a/Apps/Sandcastle/gallery/iTwin Demo.html +++ b/Apps/Sandcastle/gallery/iTwin Demo.html @@ -33,85 +33,18 @@ // must be created for the Start export route if you want to create new exports // Needs to have the mesh-export:modify scope not just mesh-export:read const accessToken = - "Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IkJlbnRsZXlJTVNfMjAyNCIsInBpLmF0bSI6ImE4bWUifQ.eyJzY29wZSI6WyJtZXNoLWV4cG9ydDpyZWFkIiwibWVzaC1leHBvcnQ6bW9kaWZ5Il0sImNsaWVudF9pZCI6Iml0d2luLWRldmVsb3Blci1jb25zb2xlIiwiYXVkIjpbImh0dHBzOi8vaW1zLmJlbnRsZXkuY29tL2FzL3Rva2VuLm9hdXRoMiIsImh0dHBzOi8vaW1zb2lkYy5iZW50bGV5LmNvbS9hcy90b2tlbi5vYXV0aDIiLCJodHRwczovL2ltc29pZGMuYmVudGxleS5jb20vcmVzb3VyY2VzIiwiYmVudGxleS1hcGktbWFuYWdlbWVudCJdLCJzdWIiOiJjMWM1MzRhNy0zZDk2LTQ2MzMtYjY5ZC1jMGEzNzE5OWQwZGUiLCJyb2xlIjoiQkVOVExFWV9FTVBMT1lFRSIsIm9yZyI6ImZhYjk3NzRiLWIzMzgtNGNjMi1hNmM5LTQ1OGJkZjdmOTY2YSIsInN1YmplY3QiOiJjMWM1MzRhNy0zZDk2LTQ2MzMtYjY5ZC1jMGEzNzE5OWQwZGUiLCJpc3MiOiJodHRwczovL2ltcy5iZW50bGV5LmNvbSIsImVudGl0bGVtZW50IjpbIkJFTlRMRVlfTEVBUk4iLCJJTlRFUk5BTCIsIlNFTEVDVF8yMDA2IiwiQkVOIiwiQkROIl0sInByZWZlcnJlZF91c2VybmFtZSI6Ikpvc2guUm91emVyQGJlbnRsZXkuY29tIiwiZ2l2ZW5fbmFtZSI6Ikpvc2giLCJzaWQiOiJHMGZTamlOXzBMRjE1Vy1IYnRTaWtoeDNuSXMuU1UxVExVSmxiblJzWlhrdFZWTS5LUDVRLjZCakNoYXptMldBMjBIdXVWMUxwNFB6RVAiLCJuYmYiOjE3MzA3NDc3NjUsInVsdGltYXRlX3NpdGUiOiIxMDAxMzg5MTE3IiwidXNhZ2VfY291bnRyeV9pc28iOiJVUyIsImF1dGhfdGltZSI6MTczMDc0ODA2NSwibmFtZSI6Ikpvc2guUm91emVyQGJlbnRsZXkuY29tIiwib3JnX25hbWUiOiJCZW50bGV5IFN5c3RlbXMgSW5jIiwiZmFtaWx5X25hbWUiOiJSb3V6ZXIiLCJlbWFpbCI6Ikpvc2guUm91emVyQGJlbnRsZXkuY29tIiwiZXhwIjoxNzMwNzUxNjY2fQ.0yEQZAyKKAdwNDiwHSD6f_Qzq0M8cbHJcMfT6JidBldw9qiyU4jx6ZdqILddrL-seWCkf9sRtWuoHm7Fw-j_wtaLASaOpHMMwC7IVdh25pbRB-D3mN8_rmQiDbXUadJ1MwH8-pNCubrER1lZLYEPrQ4zJcRtAblbJGNjFdoOi3FXB-y3JLleH4qYykLceDkbW3l2lZRfdIW2pytCIuZs7XZ9Hr6F_cLsYIrs9iRfBFVxxHxxfwgRLQoRuPmDuzKl9-ylLZUFN6CQZUfv7vzL9feoXJXGdZeGgqlyFuMOCiVbYU_elx7P7fFm8G13HvTti5hz98mV1r2bXYvfNpuklg"; + "Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IkJlbnRsZXlJTVNfMjAyNCIsInBpLmF0bSI6ImE4bWUifQ.eyJzY29wZSI6WyJtZXNoLWV4cG9ydDpyZWFkIiwibWVzaC1leHBvcnQ6bW9kaWZ5Il0sImNsaWVudF9pZCI6Iml0d2luLWRldmVsb3Blci1jb25zb2xlIiwiYXVkIjpbImh0dHBzOi8vaW1zLmJlbnRsZXkuY29tL2FzL3Rva2VuLm9hdXRoMiIsImh0dHBzOi8vaW1zb2lkYy5iZW50bGV5LmNvbS9hcy90b2tlbi5vYXV0aDIiLCJodHRwczovL2ltc29pZGMuYmVudGxleS5jb20vcmVzb3VyY2VzIiwiYmVudGxleS1hcGktbWFuYWdlbWVudCJdLCJzdWIiOiJjMWM1MzRhNy0zZDk2LTQ2MzMtYjY5ZC1jMGEzNzE5OWQwZGUiLCJyb2xlIjoiQkVOVExFWV9FTVBMT1lFRSIsIm9yZyI6ImZhYjk3NzRiLWIzMzgtNGNjMi1hNmM5LTQ1OGJkZjdmOTY2YSIsInN1YmplY3QiOiJjMWM1MzRhNy0zZDk2LTQ2MzMtYjY5ZC1jMGEzNzE5OWQwZGUiLCJpc3MiOiJodHRwczovL2ltcy5iZW50bGV5LmNvbSIsImVudGl0bGVtZW50IjpbIkJFTlRMRVlfTEVBUk4iLCJJTlRFUk5BTCIsIlNFTEVDVF8yMDA2IiwiQkVOIiwiQkROIl0sInByZWZlcnJlZF91c2VybmFtZSI6Ikpvc2guUm91emVyQGJlbnRsZXkuY29tIiwiZ2l2ZW5fbmFtZSI6Ikpvc2giLCJzaWQiOiJHMGZTamlOXzBMRjE1Vy1IYnRTaWtoeDNuSXMuU1UxVExVSmxiblJzWlhrdFZWTS5LbWlWLlF2UExVcGJyc2R3engwWGxPbkF3eTFTT0IiLCJuYmYiOjE3MzA4MzgyNDIsInVsdGltYXRlX3NpdGUiOiIxMDAxMzg5MTE3IiwidXNhZ2VfY291bnRyeV9pc28iOiJVUyIsImF1dGhfdGltZSI6MTczMDgzODU0MiwibmFtZSI6Ikpvc2guUm91emVyQGJlbnRsZXkuY29tIiwib3JnX25hbWUiOiJCZW50bGV5IFN5c3RlbXMgSW5jIiwiZmFtaWx5X25hbWUiOiJSb3V6ZXIiLCJlbWFpbCI6Ikpvc2guUm91emVyQGJlbnRsZXkuY29tIiwiZXhwIjoxNzMwODQyMTQyfQ.N_WrgjL2bqxdNLEM5nHh4Fg-FzeA-qxxpryaoaMKz8onnpgzmp-X8dyQ1TyMRqKQY99iLypE9bU45DwRJs7vBgNQ73d53SkC9TKGYn8AAOTJz8c_oEHgkoTEDaaLsMPCw88tqZ34pY0e0oIHIofVDTCvwlzwaJPADfkIxz-8GzhIt2WrXR7f6LWBFSlWNrtNhIr9Tz7UNaLOh97_3fS9KVU1I084CpBga9cj_mjGBeki7mIEQvpqMj8x2bJPae_c6WrPEjKLayrOzq4q0X0Kvle0ZFAm-Se9MFCusTFS51bYrI3IsjagGeIP2U06zzcMJ22NylomE60hz6GK_4uB3g"; Cesium.ITwin.defaultAccessToken = accessToken; // this is the iModel in the "Hello iTwinCesium" iTwin that we should all have access to // https://developer.bentley.com/my-itwins/b4a30036-0456-49ea-a439-3fcd9365e24e/home/ const imodelId = "2852c3d7-00c3-4b5d-a0ce-82bbde4f061e"; + // const imodelId = "88673c1d-12b8-48f1-8beb-5000d0edbd0b"; + + const changesetId = ""; // const knownExportId = undefined; // const knownExportId = "ab9953b2-bc8e-48ac-a5b0-5d43d68593e8"; - // async function startExport(iModelId, changesetId, accessToken) { - // console.log("Start Export"); - - // const requestOptions = { - // method: "POST", - // headers: { - // Authorization: accessToken, - // Accept: "application/vnd.bentley.itwin-platform.v1+json", - // "Content-Type": "application/json", - // }, - // body: JSON.stringify({ - // iModelId, - // changesetId, - // exportType: "CESIUM", - // }), - // }; - - // // initiate mesh export - // const response = await fetch( - // `https://api.bentley.com/mesh-export/`, - // requestOptions, - // ); - // if (!response.ok) { - // if (response.status === 401) { - // console.error("Unauthorized, bad token, wrong scopes or headers bad"); - // } else if (response.status === 403) { - // console.error("Not allowed, forbidden"); - // } else if (response.status === 422) { - // console.error("Unprocessable: Cannot create export job"); - // } else if (response.status === 429) { - // console.log("Too many requests"); - // } else { - // console.error("Bad request, unknown error", response); - // } - // return undefined; - // } - // const result = JSON.parse(JSON.stringify(await response.json())); - // return result?.export?.id; - // } - - // async function getExport(exportId, accessToken) { - // const headers = { - // Authorization: accessToken, - // Accept: "application/vnd.bentley.itwin-platform.v1+json", - // }; - - // // obtain export for specified export id - // const url = `https://api.bentley.com/mesh-export/${exportId}`; - // try { - // // TODO: this request is _really_ slow, like 7 whole second alone for me - // const response = await fetch(url, { headers }); - // if (!response.ok) { - // if (response.status === 401) { - // console.error("Unauthorized, bad token, wrong scopes or headers bad"); - // } else if (response.status === 404) { - // console.error("Requested export is not available", exportId); - // } else if (response.status === 429) { - // console.error("Too many requests"); - // } else { - // console.log("Unknown request failure", response); - // } - // return undefined; - // } - // const result = JSON.parse(JSON.stringify(await response.json())); - // return result; - // } catch (err) { - // return undefined; - // } - // } - const delay = (ms) => new Promise((res) => setTimeout(res, ms)); // Grabbed mapping from the iTwin Viewer @@ -192,69 +125,44 @@ const statusOutput = document.querySelector("#status"); async function init() { // TODO: just for testing - // await Cesium.createIModel3DTileset.deleteExport( - // "3a627319-cf6e-4535-9499-da5320a69791", - // ); - // console.log(await Cesium.createIModel3DTileset.checkForCesiumExport(imodelId)); - // console.log(await Cesium.createIModel3DTileset.getExports(imodelId)); - - const changesetId = ""; - const cesiumExport = await Cesium.createIModel3DTileset.checkForCesiumExport( - imodelId, - changesetId, - ); - let exportId = cesiumExport?.id; - if (!Cesium.defined(cesiumExport)) { - exportId = await Cesium.createIModel3DTileset.createExportForModelId( - imodelId, - changesetId, - accessToken, - ); - } + // await Cesium.ITwin.deleteExport("8bd9c52e-b379-4f7d-bf39-c45954030b26"); + // console.log(await Cesium.ITwin.getExports(imodelId)); statusOutput.innerText = "Starting export"; // const exportId = // knownExportId ?? - // (await Cesium.createIModel3DTileset.createExportForModelId( + // (await Cesium.ITwin.createExportForModelId( // imodelId, // "", // accessToken, // )); - if (!exportId) { - console.error("No export id returned"); - return; - } - console.log("Using export id", exportId); + // if (!exportId) { + // console.error("No export id returned"); + // return; + // } + // console.log("Using export id", exportId); const start = Date.now(); - // let result = await getExport(exportId, accessToken); - // let status = result.export.status; - // while (status !== "Complete") { - // await delay(5000); - // result = await getExport(exportId, accessToken); - // status = result.export.status; - // console.log(`Export is ${status}`); - - // if (Date.now() - start > 300000) { - // throw new Error("Export did not complete in time."); - // } - // } - // if (result.export.request.exportType !== "CESIUM") { - // console.error("Wrong export type", result.export.request.exportType); - // throw new Error(`Wrong export type ${result.export.request.exportType}`); - // } + statusOutput.innerText = "Creating Tileset"; - // // This link is only valid 1 hour - // let tilesetUrl = result.export._links.mesh.href; - // const splitStr = tilesetUrl.split("?"); - // // is there a cleaner way to do this? - // tilesetUrl = `${splitStr[0]}/tileset.json?${splitStr[1]}`; + let tileset = await Cesium.createIModel3DTileset.fromModelId( + imodelId, + changesetId, + ); + if (!Cesium.defined(tileset)) { + statusOutput.innerText = "Starting export"; + const exportId = await Cesium.ITwin.createExportForModelId( + imodelId, + changesetId, + accessToken, + ); + statusOutput.innerText = "Creating Tileset from export"; + tileset = await Cesium.createIModel3DTileset.fromExportId(exportId); + } - // const tileset = await Cesium.Cesium3DTileset.fromUrl(tilesetUrl); - statusOutput.innerText = "Creating Tileset"; - const tileset = await Cesium.createIModel3DTileset(exportId); + // const tileset = await Cesium.createIModel3DTileset(exportId); scene.primitives.add(tileset); tileset.colorBlendMode = Cesium.Cesium3DTileColorBlendMode.REPLACE; @@ -278,7 +186,15 @@ }, Cesium.ScreenSpaceEventType.MOUSE_MOVE); } - init(); + init().catch((error) => { + statusOutput.style.color = "red"; + if (error.message.includes("Unauthorized")) { + statusOutput.innerText = "Error: Unauthorized"; + } else { + statusOutput.innerText = "Error"; + } + console.error(error); + }); //Sandcastle_End Sandcastle.finishedLoading(); diff --git a/packages/engine/Source/Core/ITwin.js b/packages/engine/Source/Core/ITwin.js index 23826cfc5d4b..ab74ad0c5536 100644 --- a/packages/engine/Source/Core/ITwin.js +++ b/packages/engine/Source/Core/ITwin.js @@ -1,4 +1,76 @@ +import defined from "./defined.js"; +import DeveloperError from "./DeveloperError.js"; import Resource from "./Resource.js"; +import RuntimeError from "./RuntimeError.js"; + +/** + * @enum {string} + */ +const ExportStatus = Object.freeze({ + NotStarted: "NotStarted", + InProgress: "InProgress", + Complete: "Complete", + Invalid: "Invalid", +}); + +/** + * Type of an export currently, only GLTF and 3DFT are documented + * The CESIUM option is what we were told to use with Sandcastle + * I've also seen the IMODEL one but don't know where it's from + * @enum {string} + */ +const ExportType = Object.freeze({ + "3DFT": "3DFT", + GLFT: "GLTF", + IMODEL: "IMODEL", + CESIUM: "CESIUM", +}); + +/** + * @typedef {Object} GeometryOptions + * @property {boolean} includeLines + * @property {number} chordTol + * @property {number} angleTol + * @property {number} decimationTol + * @property {number} maxEdgeLength + * @property {number} minBRepFeatureSize + * @property {number} minLineStyleComponentSize + */ + +/** + * @typedef {Object} ViewDefinitionFilter + * @property {string[]} models Array of included model IDs. + * @property {string[]} categories Array of included category IDs. + * @property {string[]} neverDrawn Array of element IDs to filter out. + */ + +/** + * @typedef {Object} StartExport + * @property {string} iModelId + * @property {string} changesetId + * @property {ExportType} exportType Type of mesh to create. Currently, only GLTF and 3DFT are supported and undocumented CESIUM option + * @property {GeometryOptions} geometryOptions + * @property {ViewDefinitionFilter} viewDefinitionFilter + */ + +/** + * @typedef {Object} Link + * @property {string} href + */ + +/** + * @typedef {Object} Export + * @property {string} id + * @property {string} displayName + * @property {ExportStatus} status + * @property {StartExport} request + * @property {{mesh: Link}} _links + */ + +/** + * @typedef {Object} ExportResponse + * @property {Export} export + */ /** * Default settings for accessing the iTwin platform. @@ -34,7 +106,7 @@ const ITwin = {}; ITwin.defaultAccessToken = undefined; /** - * Gets or sets the default Google Map Tiles API endpoint. + * Gets or sets the default iTwin API endpoint. * * @type {string|Resource} * @default https://api.bentley.com @@ -43,24 +115,192 @@ ITwin.apiEndpoint = new Resource({ url: "https://api.bentley.com", }); -// TODO: this should only be needed if we have a way to generate really long term access tokens -// to sample data that is accessible to everyone -// ITwin.getDefaultTokenCredit = function (providedKey) { -// if (providedKey !== defaultAccessToken) { -// return undefined; -// } +/** + * @param {string} exportId + */ +ITwin.getExport = async function (exportId) { + if (!defined(ITwin.defaultAccessToken)) { + throw new DeveloperError("Must set ITwin.defaultAccessToken first"); + } + + const headers = { + Authorization: ITwin.defaultAccessToken, + Accept: "application/vnd.bentley.itwin-platform.v1+json", + }; + + // obtain export for specified export id + const url = `${ITwin.apiEndpoint}mesh-export/${exportId}`; + + // TODO: this request is _really_ slow, like 7 whole second alone for me + // Arun said this was kinda normal but to keep track of the `x-correlation-id` of any that take EXTRA long + const response = await fetch(url, { headers }); + if (!response.ok) { + const result = await response.json(); + if (response.status === 401) { + throw new RuntimeError( + `Unauthorized, bad token, wrong scopes or headers bad. ${result.error.details[0].code}`, + ); + } else if (response.status === 404) { + throw new RuntimeError(`Requested export is not available ${exportId}`); + } else if (response.status === 429) { + throw new RuntimeError("Too many requests"); + } + throw new RuntimeError(`Unknown request failure ${response.status}`); + } + + /** @type {ExportResponse} */ + const result = await response.json(); + return result; +}; + +/** + * Get the list of exports for the given iModel + changeset + * + * @param {string} iModelId + * @param {string} changesetId + */ +ITwin.getExports = async function (iModelId, changesetId) { + if (!defined(ITwin.defaultAccessToken)) { + throw new DeveloperError("Must set ITwin.defaultAccessToken first"); + } + + const headers = { + Authorization: ITwin.defaultAccessToken, + Accept: "application/vnd.bentley.itwin-platform.v1+json", + Prefer: "return=representation", // or return=minimal (the default) + }; + + // obtain export for specified export id + let url = `${ITwin.apiEndpoint}mesh-export/?iModelId=${iModelId}`; + if (defined(changesetId) && changesetId !== "") { + url += `&changesetId=${changesetId}`; + } -// if (!defined(defaultTokenCredit)) { -// const defaultTokenMessage = -// ' \ -// This application is using Cesium\'s default ion access token. Please assign Cesium.Ion.defaultAccessToken \ -// with an access token from your ion account before making any Cesium API calls. \ -// You can sign up for a free ion account at https://cesium.com.'; + const response = await fetch(url, { headers }); + if (!response.ok) { + const result = await response.json(); + if (response.status === 401) { + throw new RuntimeError( + `Unauthorized, bad token, wrong scopes or headers bad. ${result.error.details[0].code}`, + ); + } else if (response.status === 422) { + throw new RuntimeError( + `Unprocessable Entity:${result.error.code} ${result.error.message}`, + ); + } else if (response.status === 429) { + throw new RuntimeError("Too many requests"); + } + throw new RuntimeError(`Unknown request failure ${response.status}`); + } + + /** @type {{exports: Export[]}} */ + const result = await response.json(); + return result; +}; + +/** + * Start the export process for the given iModel + changeset. + * + * @param {string} iModelId + * @param {string} changesetId + */ +ITwin.createExportForModelId = async function (iModelId, changesetId) { + if (!defined(ITwin.defaultAccessToken)) { + throw new DeveloperError("Must set ITwin.defaultAccessToken first"); + } + + changesetId = changesetId ?? ""; + + const requestOptions = { + method: "POST", + headers: { + Authorization: ITwin.defaultAccessToken, + Accept: "application/vnd.bentley.itwin-platform.v1+json", + "Content-Type": "application/json", + }, + body: JSON.stringify({ + iModelId, + changesetId, + exportType: "CESIUM", + }), + }; + + // initiate mesh export + const response = await fetch( + `https://api.bentley.com/mesh-export/`, + requestOptions, + ); + + if (!response.ok) { + const result = await response.json(); + if (response.status === 401) { + console.error( + result.error.code, + result.error.message, + result.error.details, + ); + throw new RuntimeError( + "Unauthorized, bad token, wrong scopes or headers bad", + ); + } else if (response.status === 403) { + console.error(result.error.code, result.error.message); + throw new RuntimeError("Not allowed, forbidden"); + } else if (response.status === 422) { + console.error(result.error.code, result.error.message); + console.error(result.error.details); + throw new RuntimeError("Unprocessable: Cannot create export job"); + } else if (response.status === 429) { + throw new RuntimeError("Too many requests"); + } + + throw new RuntimeError(`Unknown request failure ${response.status}`); + } + + /** @type {ExportResponse} */ + const result = await response.json(); + return result.export.id; +}; + +/** + * Delete the specified export + * + * TODO: I'm not sure if we want this or not. Might belong better as an APP level function + * I just started creating helpers for all the routes under the `mesh-export` API + * for ease of access during testing + * + * @param {string} exportId + */ +ITwin.deleteExport = async function (exportId) { + if (!defined(ITwin.defaultAccessToken)) { + throw new DeveloperError("Must set ITwin.defaultAccessToken first"); + } + const headers = { + Authorization: ITwin.defaultAccessToken, + Accept: "application/vnd.bentley.itwin-platform.v1+json", + }; -// defaultTokenCredit = new Credit(defaultTokenMessage, true); -// } + // obtain export for specified export id + const url = `${ITwin.apiEndpoint}mesh-export/${exportId}`; -// return defaultTokenCredit; -// }; + const response = await fetch(url, { method: "DELETE", headers }); + if (!response.ok) { + const result = await response.json(); + if (response.status === 401) { + throw new RuntimeError( + `Unauthorized, bad token, wrong scopes or headers bad. ${result.error.details[0].code}`, + ); + } else if (response.status === 404) { + throw new RuntimeError("Export not found"); + } else if (response.status === 422) { + throw new RuntimeError( + `Unprocessable Entity: ${result.error.code} ${result.error.message}`, + ); + } else if (response.status === 429) { + throw new RuntimeError("Too many requests"); + } + throw new RuntimeError(`Unknown request failure ${response.status}`); + } +}; export default ITwin; +export { ExportStatus, ExportType }; diff --git a/packages/engine/Source/Scene/createIModel3DTileset.js b/packages/engine/Source/Scene/createIModel3DTileset.js index 1405aac4e5a2..7bbefa6e0e0f 100644 --- a/packages/engine/Source/Scene/createIModel3DTileset.js +++ b/packages/engine/Source/Scene/createIModel3DTileset.js @@ -1,81 +1,55 @@ import Cesium3DTileset from "./Cesium3DTileset.js"; import defined from "../Core/defined.js"; import Resource from "../Core/Resource.js"; -import ITwin from "../Core/ITwin.js"; +import ITwin, { ExportStatus, ExportType } from "../Core/ITwin.js"; import DeveloperError from "../Core/DeveloperError.js"; +import RuntimeError from "../Core/RuntimeError.js"; function delay(ms) { return new Promise((res) => setTimeout(res, ms)); } /** - * @enum {string} + * @param {Export} exportObj + * @param {Cesium3DTileset.ConstructorOptions} [options] */ -const ExportStatus = Object.freeze({ - NotStarted: "NotStarted", - InProgress: "InProgress", - Complete: "Complete", - Invalid: "Invalid", -}); +async function loadExport(exportObj, options) { + let status = exportObj.status; -/** - * Type of an export currently, only GLTF and 3DFT are documented - * The CESIUM option is what we were told to use with Sandcastle - * I've also seen the IMODEL one but don't know where it's from - * @enum {string} - */ -const ExportType = Object.freeze({ - "3DFT": "3DFT", - GLFT: "GLTF", - IMODEL: "IMODEL", - CESIUM: "CESIUM", -}); + if (exportObj.request.exportType !== ExportType.CESIUM) { + // This is an undocumented value but I think it's the only one we want to load + // TODO: should we even be checking this? + throw new Error(`Wrong export type ${exportObj.request.exportType}`); + } -/** - * @typedef {Object} GeometryOptions - * @property {boolean} includeLines - * @property {number} chordTol - * @property {number} angleTol - * @property {number} decimationTol - * @property {number} maxEdgeLength - * @property {number} minBRepFeatureSize - * @property {number} minLineStyleComponentSize - */ + const timeoutAfter = 300000; + const start = Date.now(); + // wait until the export is complete + while (status !== ExportStatus.Complete) { + await delay(5000); + exportObj = (await ITwin.getExport(exportObj.id)).export; + status = exportObj.status; + console.log(`Export is ${status}`); -/** - * @typedef {Object} ViewDefinitionFilter - * @property {string[]} models Array of included model IDs. - * @property {string[]} categories Array of included category IDs. - * @property {string[]} neverDrawn Array of element IDs to filter out. - */ + if (Date.now() - start > timeoutAfter) { + throw new RuntimeError("Export did not complete in time."); + } + } -/** - * @typedef {Object} StartExport - * @property {string} iModelId - * @property {string} changesetId - * @property {ExportType} exportType Type of mesh to create. Currently, only GLTF and 3DFT are supported and undocumented CESIUM option - * @property {GeometryOptions} geometryOptions - * @property {ViewDefinitionFilter} viewDefinitionFilter - */ + // This link is only valid 1 hour + let tilesetUrl = exportObj._links.mesh.href; + const splitStr = tilesetUrl.split("?"); + // is there a cleaner way to do this? + tilesetUrl = `${splitStr[0]}/tileset.json?${splitStr[1]}`; -/** - * @typedef {Object} Link - * @property {string} href - */ + const resource = new Resource({ + url: tilesetUrl, + }); -/** - * @typedef {Object} Export - * @property {string} id - * @property {string} displayName - * @property {ExportStatus} status - * @property {StartExport} request - * @property {{mesh: Link}} _links - */ + return Cesium3DTileset.fromUrl(resource, options); +} -/** - * @typedef {Object} ExportResponse - * @property {Export} export - */ +const createIModel3DTileset = {}; /** * Creates a {@link Cesium3DTileset} instance for the Google Photorealistic 3D Tiles tileset. @@ -101,271 +75,53 @@ const ExportType = Object.freeze({ * console.log(`Error creating tileset: ${error}`); * } */ -async function createIModel3DTileset(exportId, options) { +createIModel3DTileset.fromExportId = async function (exportId, options) { if (!defined(ITwin.defaultAccessToken)) { throw new DeveloperError("Must set ITwin.defaultAccessToken first"); } options = options ?? {}; - const timeoutAfter = 300000; - const start = Date.now(); - let result = await createIModel3DTileset.getExport(exportId); - let status = result.export.status; - - if (result.export.request.exportType !== ExportType.CESIUM) { - // This is an undocumented value but I think it's the only one we want to load - // TODO: should we even be checking this? - throw new Error(`Wrong export type ${result.export.request.exportType}`); - } - - // wait until the export is complete - while (status !== ExportStatus.Complete) { - await delay(5000); - result = await createIModel3DTileset.getExport(exportId); - status = result.export.status; - console.log(`Export is ${status}`); - - if (Date.now() - start > timeoutAfter) { - throw new Error("Export did not complete in time."); - } - } - - // This link is only valid 1 hour - let tilesetUrl = result.export._links.mesh.href; - const splitStr = tilesetUrl.split("?"); - // is there a cleaner way to do this? - tilesetUrl = `${splitStr[0]}/tileset.json?${splitStr[1]}`; - - const resource = new Resource({ - url: tilesetUrl, - }); - - return Cesium3DTileset.fromUrl(resource, options); -} - -/** - * @param {string} exportId - */ -createIModel3DTileset.getExport = async function (exportId) { - const headers = { - Authorization: ITwin.defaultAccessToken, - Accept: "application/vnd.bentley.itwin-platform.v1+json", - }; - - // obtain export for specified export id - const url = `${ITwin.apiEndpoint}mesh-export/${exportId}`; - - // TODO: this request is _really_ slow, like 7 whole second alone for me - // Arun said this was kinda normal but to keep track of the `x-correlation-id` of any that take EXTRA long - const response = await fetch(url, { headers }); - if (!response.ok) { - const result = await response.json(); - if (response.status === 401) { - throw new Error( - `Unauthorized, bad token, wrong scopes or headers bad. ${result.error.details[0].code}`, - ); - } else if (response.status === 404) { - throw new Error(`Requested export is not available ${exportId}`); - } else if (response.status === 429) { - throw new Error("Too many requests"); - } - throw new Error(`Unknown request failure ${response.status}`); - } - - /** @type {ExportResponse} */ - const result = await response.json(); - return result; -}; - -/** - * Get the list of exports for the given iModel + changeset - * - * @param {string} iModelId - * @param {string} changesetId - */ -createIModel3DTileset.getExports = async function (iModelId, changesetId) { - if (!defined(ITwin.defaultAccessToken)) { - throw new DeveloperError("Must set ITwin.defaultAccessToken first"); - } - const headers = { - Authorization: ITwin.defaultAccessToken, - Accept: "application/vnd.bentley.itwin-platform.v1+json", - Prefer: "return=representation", // or return=minimal (the default) - }; - - // obtain export for specified export id - let url = `${ITwin.apiEndpoint}mesh-export/?iModelId=${iModelId}`; - if (defined(changesetId) && changesetId !== "") { - url += `&changesetId=${changesetId}`; - } - - const response = await fetch(url, { headers }); - if (!response.ok) { - const result = await response.json(); - if (response.status === 401) { - throw new Error( - `Unauthorized, bad token, wrong scopes or headers bad. ${result.error.details[0].code}`, - ); - } else if (response.status === 422) { - throw new Error( - `Unprocessable Entity:${result.error.code} ${result.error.message}`, - ); - } else if (response.status === 429) { - throw new Error("Too many requests"); - } - throw new Error(`Unknown request failure ${response.status}`); - } - - /** @type {{exports: Export[]}} */ - const result = await response.json(); - return result; + const result = await ITwin.getExport(exportId); + const tileset = await loadExport(result.export, options); + return tileset; }; /** * Check the exports for the given iModel + changeset combination for any that - * have the desired CESIUM type and return that one + * have the desired CESIUM type and returns the first one that matches as a new tileset. * - * @param {string} iModelId - * @param {string} changesetId - */ -createIModel3DTileset.checkForCesiumExport = async function ( - iModelId, - changesetId, -) { - const { exports } = await createIModel3DTileset.getExports( - iModelId, - changesetId, - ); - const cesiumExport = exports.find( - (e) => e.request.exportType === ExportType.CESIUM, - ); - return cesiumExport; -}; - -/** - * Start the export process for the given iModel + changeset. + * If there is not a CESIUM export you can create it using {@link ITwin.createExportForModelId} * - * Pair this with the {@link checkForCesiumExport} function to avoid creating extra exports + * This function assumes one export per type per "iModel id + changeset id". If you need to create + * multiple exports per "iModel id + changeset id" you should switch to using {@link createIModel3DTileset} + * with the export id directly * * @example - * const cesiumExport = await Cesium.createIModel3DTileset.checkForCesiumExport(imodelId, changesetId); - * let exportId = cesiumExport?.id; - * if (!Cesium.defined(cesiumExport)) { - * exportId = await Cesium.createIModel3DTileset.createExportForModelId( - * imodelId, - * changesetId, - * accessToken, - * ); + * // Try to load the corresponding tileset export or create it if it doesn't exist + * let tileset = await Cesium.createIModel3DTileset.fromModelIdimodelId, changesetId); + * if (!Cesium.defined(tileset)) { + * const exportId = await Cesium.ITwin.createExportForModelId(imodelId, changesetId, accessToken); + * tileset = await Cesium.createIModel3DTileset(exportId); * } * * @param {string} iModelId * @param {string} changesetId + * @param {Cesium3DTileset.ConstructorOptions} options */ -createIModel3DTileset.createExportForModelId = async function ( +createIModel3DTileset.fromModelId = async function ( iModelId, changesetId, + options, ) { - if (!defined(ITwin.defaultAccessToken)) { - throw new DeveloperError("Must set ITwin.defaultAccessToken first"); - } - - console.log("Start Export"); - - changesetId = changesetId ?? ""; - - const requestOptions = { - method: "POST", - headers: { - Authorization: ITwin.defaultAccessToken, - Accept: "application/vnd.bentley.itwin-platform.v1+json", - "Content-Type": "application/json", - }, - body: JSON.stringify({ - iModelId, - changesetId, - exportType: "CESIUM", - }), - }; - - // initiate mesh export - const response = await fetch( - `https://api.bentley.com/mesh-export/`, - requestOptions, + const { exports } = await ITwin.getExports(iModelId, changesetId); + const cesiumExport = exports.find( + (exportObj) => exportObj.request?.exportType === ExportType.CESIUM, ); - - if (!response.ok) { - const result = await response.json(); - if (response.status === 401) { - console.error("Unauthorized, bad token, wrong scopes or headers bad"); - console.error( - result.error.code, - result.error.message, - result.error.details, - ); - } else if (response.status === 403) { - console.error("Not allowed, forbidden"); - console.error(result.error.code, result.error.message); - } else if (response.status === 422) { - console.error("Unprocessable: Cannot create export job"); - console.error(result.error.code, result.error.message); - console.error(result.error.details); - } else if (response.status === 429) { - console.log( - "Too many requests, retry after:", - response.headers.get("retry-after"), - ); - console.error(result.error.code, result.error.message); - } else { - console.error("Bad request, unknown error", response); - } - return undefined; - } - - /** @type {ExportResponse} */ - const result = await response.json(); - return result.export.id; -}; - -/** - * Delete the specified export - * - * TODO: I'm not sure if we want this or not. Might belong better as an APP level function - * I just started creating helpers for all the routes under the `mesh-export` API - * for ease of access during testing - * - * @param {string} exportId - */ -createIModel3DTileset.deleteExport = async function (exportId) { - if (!defined(ITwin.defaultAccessToken)) { - throw new DeveloperError("Must set ITwin.defaultAccessToken first"); - } - const headers = { - Authorization: ITwin.defaultAccessToken, - Accept: "application/vnd.bentley.itwin-platform.v1+json", - }; - - // obtain export for specified export id - const url = `${ITwin.apiEndpoint}mesh-export/${exportId}`; - - const response = await fetch(url, { method: "DELETE", headers }); - if (!response.ok) { - const result = await response.json(); - if (response.status === 401) { - throw new Error( - `Unauthorized, bad token, wrong scopes or headers bad. ${result.error.details[0].code}`, - ); - } else if (response.status === 404) { - throw new Error("Export not found"); - } else if (response.status === 422) { - throw new Error( - `Unprocessable Entity:${result.error.code} ${result.error.message}`, - ); - } else if (response.status === 429) { - throw new Error("Too many requests"); - } - throw new Error(`Unknown request failure ${response.status}`); + if (!defined(cesiumExport)) { + return; } + return loadExport(cesiumExport, options); }; export default createIModel3DTileset; From 4ea390891d356d89f47639bebd07e65dff34644a Mon Sep 17 00:00:00 2001 From: jjspace <8007967+jjspace@users.noreply.github.com> Date: Wed, 6 Nov 2024 14:09:02 -0500 Subject: [PATCH 036/175] small adjustments --- packages/engine/Source/Core/ITwin.js | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/packages/engine/Source/Core/ITwin.js b/packages/engine/Source/Core/ITwin.js index ab74ad0c5536..e7d49d3c59c1 100644 --- a/packages/engine/Source/Core/ITwin.js +++ b/packages/engine/Source/Core/ITwin.js @@ -21,7 +21,7 @@ const ExportStatus = Object.freeze({ */ const ExportType = Object.freeze({ "3DFT": "3DFT", - GLFT: "GLTF", + GLTF: "GLTF", IMODEL: "IMODEL", CESIUM: "CESIUM", }); @@ -75,17 +75,7 @@ const ExportType = Object.freeze({ /** * Default settings for accessing the iTwin platform. * - * Keys can be created using the iModels share routes {@link https://developer.bentley.com/apis/imodels-v2/operations/create-imodel-share/} - * - * An ion access token is only required if you are using any ion related APIs. - * A default access token is provided for evaluation purposes only. - * Sign up for a free ion account and get your own access token at {@link https://cesium.com} - * - * @see IonResource - * @see IonImageryProvider - * @see IonGeocoderService - * @see createWorldImagery - * @see createWorldTerrain + * @see createIModel3DTileset * @namespace ITwin */ const ITwin = {}; @@ -99,7 +89,7 @@ const ITwin = {}; * `mesh-export:read` for loading meshes GET /mesh-export(s) * `mesh-export:modify` if we want to include a function to create an export * `itwin-platform` if we want to use the iModel shares ourselves GET /imodels/{id}/shares - * + * Seems the `itwin-platform` scope should apply to everything but the docs are a little unclear * * @type {string|undefined} */ @@ -157,7 +147,7 @@ ITwin.getExport = async function (exportId) { * Get the list of exports for the given iModel + changeset * * @param {string} iModelId - * @param {string} changesetId + * @param {string} [changesetId] */ ITwin.getExports = async function (iModelId, changesetId) { if (!defined(ITwin.defaultAccessToken)) { @@ -202,7 +192,7 @@ ITwin.getExports = async function (iModelId, changesetId) { * Start the export process for the given iModel + changeset. * * @param {string} iModelId - * @param {string} changesetId + * @param {string} [changesetId] */ ITwin.createExportForModelId = async function (iModelId, changesetId) { if (!defined(ITwin.defaultAccessToken)) { @@ -227,7 +217,7 @@ ITwin.createExportForModelId = async function (iModelId, changesetId) { // initiate mesh export const response = await fetch( - `https://api.bentley.com/mesh-export/`, + `${ITwin.apiEndpoint}mesh-export/`, requestOptions, ); From 730c4195089418800cc7b4954f39e759df8bd416 Mon Sep 17 00:00:00 2001 From: ggetz Date: Thu, 7 Nov 2024 10:18:26 -0500 Subject: [PATCH 037/175] Add fallback spherical harmonics when dynamic environment lighting is not supported --- .../Scene/DynamicEnvironmentMapManager.js | 44 ++++- .../engine/Specs/Scene/Cesium3DTilesetSpec.js | 3 - .../Scene/DynamicEnvironmentMapManagerSpec.js | 150 +++++++++++++++--- .../Scene/Model/loadAndZoomToModelAsync.js | 18 +-- packages/engine/Specs/Scene/ShadowMapSpec.js | 16 -- 5 files changed, 168 insertions(+), 63 deletions(-) diff --git a/packages/engine/Source/Scene/DynamicEnvironmentMapManager.js b/packages/engine/Source/Scene/DynamicEnvironmentMapManager.js index d8d140ce29f9..2b6abef67c5d 100644 --- a/packages/engine/Source/Scene/DynamicEnvironmentMapManager.js +++ b/packages/engine/Source/Scene/DynamicEnvironmentMapManager.js @@ -95,7 +95,8 @@ function DynamicEnvironmentMapManager(options) { this._radianceCubeMap = undefined; this._irradianceMapTexture = undefined; - this._sphericalHarmonicCoefficients = new Array(9); + this._sphericalHarmonicCoefficients = + DynamicEnvironmentMapManager.DEAFULT_SPHERICAL_HARMONIC_COEFFICIENTS.slice(); this._lastTime = new JulianDate(); const width = Math.pow(2, mipmapLevels - 1); @@ -727,8 +728,11 @@ function updateSphericalHarmonicCoefficients(manager, frameState) { */ DynamicEnvironmentMapManager.prototype.update = function (frameState) { const mode = frameState.mode; + const isSupported = + DynamicEnvironmentMapManager.isDynamicUpdateSupported(frameState); if ( + !isSupported || !this.enabled || !this.shouldUpdate || !defined(this._position) || @@ -841,12 +845,50 @@ DynamicEnvironmentMapManager.prototype.destroy = function () { return destroyObject(this); }; +/** + * Returns true if dynamic updates are supported in the current WebGL rendering context. + * Dynamic updates requires the EXT_color_buffer_float or EXT_color_buffer_half_float extension. + * + * @param {Scene|FrameState} contextOption The object containing the rendering context + * @returns {boolean} true if supported + */ +DynamicEnvironmentMapManager.isDynamicUpdateSupported = function ( + contextOption, +) { + const context = contextOption.context; + return context.halfFloatingPointTexture || context.colorBufferFloat; +}; + /** * Average hue of ground color on earth, a warm green-gray. * @type {Color} + * @readonly */ DynamicEnvironmentMapManager.AVERAGE_EARTH_GROUND_COLOR = Object.freeze( Color.fromCssColorString("#717145"), ); +/** + * The default third order spherical harmonic coefficients used for the diffuse color of image-based lighting, a white ambient light with low intensity. + *

+ * There are nine Cartesian3 coefficients. + * The order of the coefficients is: L0,0, L1,-1, L1,0, L1,1, L2,-2, L2,-1, L2,0, L2,1, L2,2 + *

+ * @readonly + * @type {Cartesian3[]} + * @see {@link https://graphics.stanford.edu/papers/envmap/envmap.pdf|An Efficient Representation for Irradiance Environment Maps} + */ +DynamicEnvironmentMapManager.DEAFULT_SPHERICAL_HARMONIC_COEFFICIENTS = + Object.freeze([ + Object.freeze(new Cartesian3(0.35449, 0.35449, 0.35449)), + Cartesian3.ZERO, + Cartesian3.ZERO, + Cartesian3.ZERO, + Cartesian3.ZERO, + Cartesian3.ZERO, + Cartesian3.ZERO, + Cartesian3.ZERO, + Cartesian3.ZERO, + ]); + export default DynamicEnvironmentMapManager; diff --git a/packages/engine/Specs/Scene/Cesium3DTilesetSpec.js b/packages/engine/Specs/Scene/Cesium3DTilesetSpec.js index 1f70a75175c3..320870826e3e 100644 --- a/packages/engine/Specs/Scene/Cesium3DTilesetSpec.js +++ b/packages/engine/Specs/Scene/Cesium3DTilesetSpec.js @@ -211,9 +211,6 @@ describe( options = { cullRequestsWhileMoving: false, - environmentMapOptions: { - enabled: scene.highDynamicRangeSupported, - }, }; }); diff --git a/packages/engine/Specs/Scene/DynamicEnvironmentMapManagerSpec.js b/packages/engine/Specs/Scene/DynamicEnvironmentMapManagerSpec.js index 57ea8ee5354f..7647e76fbe1d 100644 --- a/packages/engine/Specs/Scene/DynamicEnvironmentMapManagerSpec.js +++ b/packages/engine/Specs/Scene/DynamicEnvironmentMapManagerSpec.js @@ -31,6 +31,40 @@ describe("Scene/DynamicEnvironmentMapManager", function () { expect(manager.groundAlbedo).toBe(0.31); }); + it("constructs with options", function () { + const manager = new DynamicEnvironmentMapManager({ + enabled: false, + maximumSecondsDifference: 0, + maximumPositionEpsilon: 0, + atmosphereScatteringIntensity: 0, + gamma: 0, + brightness: 0, + saturation: 0, + groundColor: Color.BLUE, + groundAlbedo: 0, + }); + + expect(manager.enabled).toBeFalse(); + expect(manager.shouldUpdate).toBeTrue(); + expect(manager.maximumSecondsDifference).toBe(0); + expect(manager.maximumPositionEpsilon).toBe(0); + expect(manager.atmosphereScatteringIntensity).toBe(0); + expect(manager.gamma).toBe(0); + expect(manager.brightness).toBe(0); + expect(manager.saturation).toBe(0); + expect(manager.groundColor).toEqual(Color.BLUE); + expect(manager.groundAlbedo).toBe(0); + }); + + it("uses default spherical harmonic coefficients", () => { + const manager = new DynamicEnvironmentMapManager(); + + expect(manager.sphericalHarmonicCoefficients.length).toBe(9); + expect(manager.sphericalHarmonicCoefficients).toEqual( + DynamicEnvironmentMapManager.DEAFULT_SPHERICAL_HARMONIC_COEFFICIENTS, + ); + }); + describe( "render tests", () => { @@ -53,7 +87,7 @@ describe("Scene/DynamicEnvironmentMapManager", function () { scene.atmosphere = new Atmosphere(); }); - // Allows the compute commands to be added to the command list at the right point in the pipeline + // A pared-down Primitive. Allows the compute commands to be added to the command list at the right point in the pipeline. function EnvironmentMockPrimitive(manager) { this.update = function (frameState) { manager.update(frameState); @@ -64,8 +98,74 @@ describe("Scene/DynamicEnvironmentMapManager", function () { }; } + it("does not update if position is undefined", async function () { + const manager = new DynamicEnvironmentMapManager(); + + const primitive = new EnvironmentMockPrimitive(manager); + scene.primitives.add(primitive); + + scene.renderForSpecs(); + + expect(manager.radianceCubeMap).toBeUndefined(); + + scene.renderForSpecs(); + + expect(manager.sphericalHarmonicCoefficients).toEqual( + DynamicEnvironmentMapManager.DEAFULT_SPHERICAL_HARMONIC_COEFFICIENTS, + ); + }); + + it("does not update if enabled is false", async function () { + const manager = new DynamicEnvironmentMapManager(); + + const cartographic = Cartographic.fromDegrees(-75.165222, 39.952583); + manager.position = + Ellipsoid.WGS84.cartographicToCartesian(cartographic); + manager.enabled = false; + + const primitive = new EnvironmentMockPrimitive(manager); + scene.primitives.add(primitive); + + scene.renderForSpecs(); + + expect(manager.radianceCubeMap).toBeUndefined(); + + scene.renderForSpecs(); + + expect(manager.sphericalHarmonicCoefficients).toEqual( + DynamicEnvironmentMapManager.DEAFULT_SPHERICAL_HARMONIC_COEFFICIENTS, + ); + }); + + it("does not update if requires extensions are not available", async function () { + spyOn( + DynamicEnvironmentMapManager, + "isDynamicUpdateSupported", + ).and.returnValue(false); + + const manager = new DynamicEnvironmentMapManager(); + + const cartographic = Cartographic.fromDegrees(-75.165222, 39.952583); + manager.position = + Ellipsoid.WGS84.cartographicToCartesian(cartographic); + + const primitive = new EnvironmentMockPrimitive(manager); + scene.primitives.add(primitive); + + scene.renderForSpecs(); + + expect(manager.radianceCubeMap).toBeUndefined(); + + scene.renderForSpecs(); + + expect(manager.sphericalHarmonicCoefficients).toEqual( + DynamicEnvironmentMapManager.DEAFULT_SPHERICAL_HARMONIC_COEFFICIENTS, + ); + }); + it("creates environment map and spherical harmonics at surface in Philadelphia with static lighting", async function () { - if (!scene.highDynamicRangeSupported) { + // Skip if required WebGL extensions are not supported + if (!DynamicEnvironmentMapManager.isDynamicUpdateSupported(scene)) { return; } @@ -146,7 +246,8 @@ describe("Scene/DynamicEnvironmentMapManager", function () { }); it("creates environment map and spherical harmonics at altitude in Philadelphia with static lighting", async function () { - if (!scene.highDynamicRangeSupported) { + // Skip if required WebGL extensions are not supported + if (!DynamicEnvironmentMapManager.isDynamicUpdateSupported(scene)) { return; } @@ -231,7 +332,8 @@ describe("Scene/DynamicEnvironmentMapManager", function () { }); it("creates environment map and spherical harmonics above Earth's atmosphere with static lighting", async function () { - if (!scene.highDynamicRangeSupported) { + // Skip if required WebGL extensions are not supported + if (!DynamicEnvironmentMapManager.isDynamicUpdateSupported(scene)) { return; } @@ -317,7 +419,8 @@ describe("Scene/DynamicEnvironmentMapManager", function () { }); it("creates environment map and spherical harmonics at surface in Philadelphia with dynamic lighting", async function () { - if (!scene.highDynamicRangeSupported) { + // Skip if required WebGL extensions are not supported + if (!DynamicEnvironmentMapManager.isDynamicUpdateSupported(scene)) { return; } @@ -401,7 +504,8 @@ describe("Scene/DynamicEnvironmentMapManager", function () { }); it("creates environment map and spherical harmonics at surface in Sydney with dynamic lighting", async function () { - if (!scene.highDynamicRangeSupported) { + // Skip if required WebGL extensions are not supported + if (!DynamicEnvironmentMapManager.isDynamicUpdateSupported(scene)) { return; } @@ -485,7 +589,8 @@ describe("Scene/DynamicEnvironmentMapManager", function () { }); it("lighting uses atmosphere properties", async function () { - if (!scene.highDynamicRangeSupported) { + // Skip if required WebGL extensions are not supported + if (!DynamicEnvironmentMapManager.isDynamicUpdateSupported(scene)) { return; } @@ -572,10 +677,10 @@ describe("Scene/DynamicEnvironmentMapManager", function () { }); it("lighting uses atmosphereScatteringIntensity value", async function () { - if (!scene.highDynamicRangeSupported) { + // Skip if required WebGL extensions are not supported + if (!DynamicEnvironmentMapManager.isDynamicUpdateSupported(scene)) { return; } - const manager = new DynamicEnvironmentMapManager(); manager.atmosphereScatteringIntensity = 1.0; @@ -654,7 +759,8 @@ describe("Scene/DynamicEnvironmentMapManager", function () { }); it("lighting uses gamma value", async function () { - if (!scene.highDynamicRangeSupported) { + // Skip if required WebGL extensions are not supported + if (!DynamicEnvironmentMapManager.isDynamicUpdateSupported(scene)) { return; } @@ -736,7 +842,8 @@ describe("Scene/DynamicEnvironmentMapManager", function () { }); it("lighting uses brightness value", async function () { - if (!scene.highDynamicRangeSupported) { + // Skip if required WebGL extensions are not supported + if (!DynamicEnvironmentMapManager.isDynamicUpdateSupported(scene)) { return; } @@ -818,7 +925,8 @@ describe("Scene/DynamicEnvironmentMapManager", function () { }); it("lighting uses saturation value", async function () { - if (!scene.highDynamicRangeSupported) { + // Skip if required WebGL extensions are not supported + if (!DynamicEnvironmentMapManager.isDynamicUpdateSupported(scene)) { return; } @@ -900,7 +1008,8 @@ describe("Scene/DynamicEnvironmentMapManager", function () { }); it("lighting uses ground color value", async function () { - if (!scene.highDynamicRangeSupported) { + // Skip if required WebGL extensions are not supported + if (!DynamicEnvironmentMapManager.isDynamicUpdateSupported(scene)) { return; } @@ -982,7 +1091,8 @@ describe("Scene/DynamicEnvironmentMapManager", function () { }); it("lighting uses ground albedo value", async function () { - if (!scene.highDynamicRangeSupported) { + // Skip if required WebGL extensions are not supported + if (!DynamicEnvironmentMapManager.isDynamicUpdateSupported(scene)) { return; } @@ -1064,18 +1174,6 @@ describe("Scene/DynamicEnvironmentMapManager", function () { }); it("destroys", function () { - if (!scene.highDynamicRangeSupported) { - const manager = new DynamicEnvironmentMapManager(); - const cartographic = Cartographic.fromDegrees(-75.165222, 39.952583); - manager.position = - Ellipsoid.WGS84.cartographicToCartesian(cartographic); - - manager.destroy(); - - expect(manager.isDestroyed()).toBe(true); - return; - } - const manager = new DynamicEnvironmentMapManager(); const cartographic = Cartographic.fromDegrees(-75.165222, 39.952583); manager.position = diff --git a/packages/engine/Specs/Scene/Model/loadAndZoomToModelAsync.js b/packages/engine/Specs/Scene/Model/loadAndZoomToModelAsync.js index e50ab6a8cf5c..514f53df64f5 100644 --- a/packages/engine/Specs/Scene/Model/loadAndZoomToModelAsync.js +++ b/packages/engine/Specs/Scene/Model/loadAndZoomToModelAsync.js @@ -1,28 +1,12 @@ -import { Model, ImageBasedLighting, Cartesian3 } from "../../../index.js"; +import { Model } from "../../../index.js"; import pollToPromise from "../../../../../Specs/pollToPromise.js"; -// A white ambient light with low intensity -const defaultIbl = new ImageBasedLighting({ - sphericalHarmonicCoefficients: [ - new Cartesian3(0.35449, 0.35449, 0.35449), - Cartesian3.ZERO, - Cartesian3.ZERO, - Cartesian3.ZERO, - Cartesian3.ZERO, - Cartesian3.ZERO, - Cartesian3.ZERO, - Cartesian3.ZERO, - Cartesian3.ZERO, - ], -}); - async function loadAndZoomToModelAsync(options, scene) { options = { environmentMapOptions: { enabled: false, // disable other diffuse lighting by default ...options.environmentMapOptions, }, - imageBasedLighting: defaultIbl, ...options, }; diff --git a/packages/engine/Specs/Scene/ShadowMapSpec.js b/packages/engine/Specs/Scene/ShadowMapSpec.js index 2a66f528b10d..1b142d13152b 100644 --- a/packages/engine/Specs/Scene/ShadowMapSpec.js +++ b/packages/engine/Specs/Scene/ShadowMapSpec.js @@ -11,7 +11,6 @@ import { HeadingPitchRange, HeadingPitchRoll, HeightmapTerrainData, - ImageBasedLighting, JulianDate, Math as CesiumMath, Matrix4, @@ -282,26 +281,11 @@ describe( } async function loadModel(options) { - // A white ambient light with low intensity - const defaultIbl = new ImageBasedLighting({ - sphericalHarmonicCoefficients: [ - new Cartesian3(0.35449, 0.35449, 0.35449), - Cartesian3.ZERO, - Cartesian3.ZERO, - Cartesian3.ZERO, - Cartesian3.ZERO, - Cartesian3.ZERO, - Cartesian3.ZERO, - Cartesian3.ZERO, - Cartesian3.ZERO, - ], - }); const model = scene.primitives.add( await Model.fromGltfAsync({ environmentMapOptions: { enabled: false, }, - imageBasedLighting: defaultIbl, ...options, }), ); From 83860b6dc733ae12fac3abe80a130eccc880d77f Mon Sep 17 00:00:00 2001 From: ggetz Date: Thu, 7 Nov 2024 11:04:30 -0500 Subject: [PATCH 038/175] Update CHANGES.md, fix typo --- CHANGES.md | 9 +++++++++ .../engine/Source/Scene/DynamicEnvironmentMapManager.js | 6 +++--- .../Specs/Scene/DynamicEnvironmentMapManagerSpec.js | 8 ++++---- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 0cdd0ec2e29c..e3107c706259 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,14 @@ # Change Log +### 1.123.1 - 2024-11-07 + +#### @cesium/engine + +##### Additions :tada: + +- Added fallback diffuse lighting, `DynamicEnvironmentMapManager.DEFAULT_SPHERICAL_HARMONIC_COEFFICIENTS`, that is used when `DynamicEnvironmentMapManager` is disabled or unsupported. [#12292](https://github.com/CesiumGS/cesium/pull/12292) +- Added `DynamicEnvironmentMapManager.isDynamicUpdateSupported` to check if dynamic environment map updates are supported. [#12292](https://github.com/CesiumGS/cesium/pull/12292) + ### 1.123 - 2024-11-01 #### @cesium/engine diff --git a/packages/engine/Source/Scene/DynamicEnvironmentMapManager.js b/packages/engine/Source/Scene/DynamicEnvironmentMapManager.js index 2b6abef67c5d..f37e038755c1 100644 --- a/packages/engine/Source/Scene/DynamicEnvironmentMapManager.js +++ b/packages/engine/Source/Scene/DynamicEnvironmentMapManager.js @@ -96,7 +96,7 @@ function DynamicEnvironmentMapManager(options) { this._irradianceMapTexture = undefined; this._sphericalHarmonicCoefficients = - DynamicEnvironmentMapManager.DEAFULT_SPHERICAL_HARMONIC_COEFFICIENTS.slice(); + DynamicEnvironmentMapManager.DEFAULT_SPHERICAL_HARMONIC_COEFFICIENTS.slice(); this._lastTime = new JulianDate(); const width = Math.pow(2, mipmapLevels - 1); @@ -849,7 +849,7 @@ DynamicEnvironmentMapManager.prototype.destroy = function () { * Returns true if dynamic updates are supported in the current WebGL rendering context. * Dynamic updates requires the EXT_color_buffer_float or EXT_color_buffer_half_float extension. * - * @param {Scene|FrameState} contextOption The object containing the rendering context + * @param {Scene} contextOption The object containing the rendering context * @returns {boolean} true if supported */ DynamicEnvironmentMapManager.isDynamicUpdateSupported = function ( @@ -878,7 +878,7 @@ DynamicEnvironmentMapManager.AVERAGE_EARTH_GROUND_COLOR = Object.freeze( * @type {Cartesian3[]} * @see {@link https://graphics.stanford.edu/papers/envmap/envmap.pdf|An Efficient Representation for Irradiance Environment Maps} */ -DynamicEnvironmentMapManager.DEAFULT_SPHERICAL_HARMONIC_COEFFICIENTS = +DynamicEnvironmentMapManager.DEFAULT_SPHERICAL_HARMONIC_COEFFICIENTS = Object.freeze([ Object.freeze(new Cartesian3(0.35449, 0.35449, 0.35449)), Cartesian3.ZERO, diff --git a/packages/engine/Specs/Scene/DynamicEnvironmentMapManagerSpec.js b/packages/engine/Specs/Scene/DynamicEnvironmentMapManagerSpec.js index 7647e76fbe1d..de82af7a9478 100644 --- a/packages/engine/Specs/Scene/DynamicEnvironmentMapManagerSpec.js +++ b/packages/engine/Specs/Scene/DynamicEnvironmentMapManagerSpec.js @@ -61,7 +61,7 @@ describe("Scene/DynamicEnvironmentMapManager", function () { expect(manager.sphericalHarmonicCoefficients.length).toBe(9); expect(manager.sphericalHarmonicCoefficients).toEqual( - DynamicEnvironmentMapManager.DEAFULT_SPHERICAL_HARMONIC_COEFFICIENTS, + DynamicEnvironmentMapManager.DEFAULT_SPHERICAL_HARMONIC_COEFFICIENTS, ); }); @@ -111,7 +111,7 @@ describe("Scene/DynamicEnvironmentMapManager", function () { scene.renderForSpecs(); expect(manager.sphericalHarmonicCoefficients).toEqual( - DynamicEnvironmentMapManager.DEAFULT_SPHERICAL_HARMONIC_COEFFICIENTS, + DynamicEnvironmentMapManager.DEFAULT_SPHERICAL_HARMONIC_COEFFICIENTS, ); }); @@ -133,7 +133,7 @@ describe("Scene/DynamicEnvironmentMapManager", function () { scene.renderForSpecs(); expect(manager.sphericalHarmonicCoefficients).toEqual( - DynamicEnvironmentMapManager.DEAFULT_SPHERICAL_HARMONIC_COEFFICIENTS, + DynamicEnvironmentMapManager.DEFAULT_SPHERICAL_HARMONIC_COEFFICIENTS, ); }); @@ -159,7 +159,7 @@ describe("Scene/DynamicEnvironmentMapManager", function () { scene.renderForSpecs(); expect(manager.sphericalHarmonicCoefficients).toEqual( - DynamicEnvironmentMapManager.DEAFULT_SPHERICAL_HARMONIC_COEFFICIENTS, + DynamicEnvironmentMapManager.DEFAULT_SPHERICAL_HARMONIC_COEFFICIENTS, ); }); From 91b73bd2d6f5bf3f53d940d90050cc2449111485 Mon Sep 17 00:00:00 2001 From: ggetz Date: Thu, 7 Nov 2024 14:25:12 -0500 Subject: [PATCH 039/175] Feedback on environmt map fix --- .../Source/Scene/DynamicEnvironmentMapManager.js | 10 +++++----- packages/engine/Specs/Scene/Model/ModelSpec.js | 11 +++++++++-- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/packages/engine/Source/Scene/DynamicEnvironmentMapManager.js b/packages/engine/Source/Scene/DynamicEnvironmentMapManager.js index f37e038755c1..40fd2560f1be 100644 --- a/packages/engine/Source/Scene/DynamicEnvironmentMapManager.js +++ b/packages/engine/Source/Scene/DynamicEnvironmentMapManager.js @@ -729,6 +729,8 @@ function updateSphericalHarmonicCoefficients(manager, frameState) { DynamicEnvironmentMapManager.prototype.update = function (frameState) { const mode = frameState.mode; const isSupported = + // A FrameState type works here because the function only references the context parameter. + // @ts-ignore DynamicEnvironmentMapManager.isDynamicUpdateSupported(frameState); if ( @@ -849,13 +851,11 @@ DynamicEnvironmentMapManager.prototype.destroy = function () { * Returns true if dynamic updates are supported in the current WebGL rendering context. * Dynamic updates requires the EXT_color_buffer_float or EXT_color_buffer_half_float extension. * - * @param {Scene} contextOption The object containing the rendering context + * @param {Scene} scene The object containing the rendering context * @returns {boolean} true if supported */ -DynamicEnvironmentMapManager.isDynamicUpdateSupported = function ( - contextOption, -) { - const context = contextOption.context; +DynamicEnvironmentMapManager.isDynamicUpdateSupported = function (scene) { + const context = scene.context; return context.halfFloatingPointTexture || context.colorBufferFloat; }; diff --git a/packages/engine/Specs/Scene/Model/ModelSpec.js b/packages/engine/Specs/Scene/Model/ModelSpec.js index 2f31aa01b5fa..7af9758fdad9 100644 --- a/packages/engine/Specs/Scene/Model/ModelSpec.js +++ b/packages/engine/Specs/Scene/Model/ModelSpec.js @@ -3113,10 +3113,13 @@ describe( { gltf: boxTexturedGltfUrl, lightColor: Cartesian3.ZERO, - imageBasedLighting: undefined, }, scene, ); + + // ignore any image-based lighting– Test directional light only + model.imageBasedLighting.imageBasedLightingFactor = Cartesian2.ZERO; + verifyRender(model, false); }); @@ -3125,6 +3128,10 @@ describe( { gltf: boxTexturedGltfUrl, imageBasedLighting: undefined }, scene, ); + + // ignore any image-based lighting– Test directional light only + model.imageBasedLighting.imageBasedLightingFactor = Cartesian2.ZERO; + model.lightColor = Cartesian3.ZERO; verifyRender(model, false); @@ -3193,7 +3200,7 @@ describe( it("changing imageBasedLighting works", async function () { const imageBasedLighting = new ImageBasedLighting({ sphericalHarmonicCoefficients: [ - new Cartesian3(0.35449, 0.35449, 0.35449), + new Cartesian3(1.0, 0.0, 0.0), Cartesian3.ZERO, Cartesian3.ZERO, Cartesian3.ZERO, From a2cde2959977980bfb7db6eed72796b0d63fff9c Mon Sep 17 00:00:00 2001 From: lukemckinstry Date: Thu, 7 Nov 2024 16:51:49 -0500 Subject: [PATCH 040/175] updates for 1.123.1 release --- package.json | 6 +++--- packages/engine/package.json | 2 +- packages/widgets/package.json | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 9ed3be76bceb..b78831a46e6e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cesium", - "version": "1.123.0", + "version": "1.123.1", "description": "CesiumJS is a JavaScript library for creating 3D globes and 2D maps in a web browser without a plugin.", "homepage": "http://cesium.com/cesiumjs/", "license": "Apache-2.0", @@ -51,8 +51,8 @@ "./Specs/**/*" ], "dependencies": { - "@cesium/engine": "^12.0.0", - "@cesium/widgets": "^9.0.0" + "@cesium/engine": "^12.0.1", + "@cesium/widgets": "^9.0.1" }, "devDependencies": { "@playwright/test": "^1.41.1", diff --git a/packages/engine/package.json b/packages/engine/package.json index acc1c285479d..8d242b4e4350 100644 --- a/packages/engine/package.json +++ b/packages/engine/package.json @@ -1,6 +1,6 @@ { "name": "@cesium/engine", - "version": "12.0.0", + "version": "12.0.1", "description": "CesiumJS is a JavaScript library for creating 3D globes and 2D maps in a web browser without a plugin.", "keywords": [ "3D", diff --git a/packages/widgets/package.json b/packages/widgets/package.json index 42736b0c563a..f980eee7f6db 100644 --- a/packages/widgets/package.json +++ b/packages/widgets/package.json @@ -1,6 +1,6 @@ { "name": "@cesium/widgets", - "version": "9.0.0", + "version": "9.0.1", "description": "A widgets library for use with CesiumJS. CesiumJS is a JavaScript library for creating 3D globes and 2D maps in a web browser without a plugin.", "keywords": [ "3D", @@ -28,7 +28,7 @@ "node": ">=14.0.0" }, "dependencies": { - "@cesium/engine": "^12.0.0", + "@cesium/engine": "^12.0.1", "nosleep.js": "^0.12.0" }, "type": "module", From 4dfc79aee87bb340e802000e91db5e82dc767a8c Mon Sep 17 00:00:00 2001 From: lukemckinstry Date: Thu, 7 Nov 2024 16:54:10 -0500 Subject: [PATCH 041/175] update third party --- ThirdParty.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ThirdParty.json b/ThirdParty.json index 99be04b43dfc..10a55aa1d64e 100644 --- a/ThirdParty.json +++ b/ThirdParty.json @@ -77,7 +77,7 @@ "license": [ "MIT" ], - "version": "1.3.9", + "version": "1.4.0", "url": "https://www.npmjs.com/package/jsep" }, { From 2978c699c0a6e2469f792b68195cc880ff5c883d Mon Sep 17 00:00:00 2001 From: Emanuele Mastaglia Date: Fri, 8 Nov 2024 09:52:54 +0100 Subject: [PATCH 042/175] fix(widget): close debug pragma around baseLayer validation --- packages/widgets/Source/Viewer/Viewer.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/widgets/Source/Viewer/Viewer.js b/packages/widgets/Source/Viewer/Viewer.js index 499a8d35ad07..f10d767a0b86 100644 --- a/packages/widgets/Source/Viewer/Viewer.js +++ b/packages/widgets/Source/Viewer/Viewer.js @@ -410,6 +410,7 @@ function Viewer(container, options) { ) { throw new DeveloperError("Cannot use baseLayer when globe is disabled."); } + //>>includeEnd('debug') const createBaseLayerPicker = (!defined(options.globe) || options.globe !== false) && From 76cd99ce0ecc3c46d897f567b364fd4182a3fd58 Mon Sep 17 00:00:00 2001 From: "jerome.fayot" Date: Sun, 15 Sep 2024 15:03:30 +0200 Subject: [PATCH 043/175] feat: added Entity.trackingReferenceFrame to allow tracking entities in their own inertial reference frame --- .../gallery/Callback Position Property.html | 32 ++-------- Apps/Sandcastle/gallery/Interpolation.html | 15 +++++ CHANGES.md | 8 +++ packages/engine/Source/DataSources/Entity.js | 14 +++++ .../engine/Source/DataSources/EntityView.js | 58 ++++++++++++++++--- .../engine/Specs/DataSources/EntitySpec.js | 4 ++ .../Specs/DataSources/EntityViewSpec.js | 23 ++++++++ 7 files changed, 119 insertions(+), 35 deletions(-) diff --git a/Apps/Sandcastle/gallery/Callback Position Property.html b/Apps/Sandcastle/gallery/Callback Position Property.html index 272509b8b934..6f748d3fe970 100644 --- a/Apps/Sandcastle/gallery/Callback Position Property.html +++ b/Apps/Sandcastle/gallery/Callback Position Property.html @@ -134,7 +134,7 @@ } // Create the entity and bind its position to the callback position property - viewer.entities.add({ + const entity = viewer.entities.add({ availability: new Cesium.TimeIntervalCollection([ new Cesium.TimeInterval({ start: start, @@ -158,35 +158,11 @@ leadTime: 1, trailTime: 0.1, }, + trackingReferenceFrame: Cesium.ReferenceFrame.INERTIAL, + viewFrom: new Cesium.Cartesian3(-100, 0, 10), }); - const camera = viewer.camera; - const scene = viewer.scene; - - const scratchPosition = new Cesium.Cartesian3(); - const scratchOrientation = new Cesium.Quaternion(); - const scratchTransform = new Cesium.Matrix4(); - const offset = new Cesium.Cartesian3(-100, 0, 10); - - // Update camera to follow entity's position and orientation - viewer.clock.onTick.addEventListener(function (clock) { - if (scene.mode === Cesium.Scene.MORPHING) { - return; - } - const time = clock.currentTime; - const entityPosition = position.getValue(time, scratchPosition); - const entityOrientation = orientation.getValue(time, scratchOrientation); - if (entityPosition === undefined || entityOrientation === undefined) { - return; - } - const transform = Cesium.Matrix4.fromTranslationQuaternionRotationScale( - entityPosition, - entityOrientation, - Cesium.Cartesian3.ONE, - scratchTransform, - ); - camera.lookAtTransform(transform, offset); - }); + viewer.trackedEntity = entity; //Sandcastle_End }; if (typeof Cesium !== "undefined") { diff --git a/Apps/Sandcastle/gallery/Interpolation.html b/Apps/Sandcastle/gallery/Interpolation.html index d0559b27d7a8..409ee4485148 100644 --- a/Apps/Sandcastle/gallery/Interpolation.html +++ b/Apps/Sandcastle/gallery/Interpolation.html @@ -154,6 +154,21 @@ viewer.trackedEntity = entity; }); + Sandcastle.addToolbarMenu([ + { + text: "Tracking reference frame: Fixed", + onselect: function () { + entity.trackingReferenceFrame = Cesium.ReferenceFrame.FIXED; + }, + }, + { + text: "Tracking reference frame: Inertial", + onselect: function () { + entity.trackingReferenceFrame = Cesium.ReferenceFrame.INERTIAL; + }, + }, + ]); + //Add a combo box for selecting each interpolation mode. Sandcastle.addToolbarMenu( [ diff --git a/CHANGES.md b/CHANGES.md index e3107c706259..dcff933ce4ad 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,13 @@ # Change Log +### 1.124 - 2024-12-01 + +#### @cesium/engine + +##### Additions :tada: + +- Added `Entity.trackingReferenceFrame` property to allow tracking entities in their own inertial reference frame. [#12194](https://github.com/CesiumGS/cesium/pull/12194) + ### 1.123.1 - 2024-11-07 #### @cesium/engine diff --git a/packages/engine/Source/DataSources/Entity.js b/packages/engine/Source/DataSources/Entity.js index b2b7433e29ba..ed35b3a13da3 100644 --- a/packages/engine/Source/DataSources/Entity.js +++ b/packages/engine/Source/DataSources/Entity.js @@ -10,6 +10,7 @@ import CesiumMath from "../Core/Math.js"; import Matrix3 from "../Core/Matrix3.js"; import Matrix4 from "../Core/Matrix4.js"; import Quaternion from "../Core/Quaternion.js"; +import ReferenceFrame from "../Core/ReferenceFrame.js"; import Transforms from "../Core/Transforms.js"; import GroundPolylinePrimitive from "../Scene/GroundPolylinePrimitive.js"; import GroundPrimitive from "../Scene/GroundPrimitive.js"; @@ -73,6 +74,7 @@ function createPropertyTypeDescriptor(name, Type) { * @property {string} [name] A human readable name to display to users. It does not have to be unique. * @property {TimeIntervalCollection} [availability] The availability, if any, associated with this object. * @property {boolean} [show] A boolean value indicating if the entity and its children are displayed. + * @property {ReferenceFrame} [trackingReferenceFrame=ReferenceFrame.FIXED] The reference frame used when this entity is being tracked. If undefined, east-north-up at entity's position is used. When set to ReferenceFrame.INERTIAL, the camera will track and rotate according to entity's position and orirentation. * @property {Property | string} [description] A string Property specifying an HTML description for this entity. * @property {PositionProperty | Cartesian3 | CallbackProperty} [position] A Property specifying the entity position. * @property {Property | Quaternion} [orientation=Transforms.eastNorthUpToFixedFrame(position)] A Property specifying the entity orientation in respect to Earth-fixed-Earth-centered (ECEF). If undefined, east-north-up at entity position is used. @@ -122,6 +124,10 @@ function Entity(options) { this._definitionChanged = new Event(); this._name = options.name; this._show = defaultValue(options.show, true); + this._trackingReferenceFrame = defaultValue( + options.trackingReferenceFrame, + ReferenceFrame.FIXED + ); this._parent = undefined; this._propertyNames = [ "billboard", @@ -296,6 +302,14 @@ Object.defineProperties(Entity.prototype, { this._definitionChanged.raiseEvent(this, "show", value, !value); }, }, + /** + * Gets or sets the entity's tracking reference frame. + * @demo {@link https://sandcastle.cesium.com/index.html?src=Interpolation.html|Cesium Sandcastle Interpolation Demo} + * + * @memberof Entity.prototype + * @type {ReferenceFrame} + */ + trackingReferenceFrame: createRawPropertyDescriptor("trackingReferenceFrame"), /** * Gets whether this entity is being displayed, taking into account * the visibility of any ancestor entities. diff --git a/packages/engine/Source/DataSources/EntityView.js b/packages/engine/Source/DataSources/EntityView.js index eb4fd4c5c207..b33b61e29111 100644 --- a/packages/engine/Source/DataSources/EntityView.js +++ b/packages/engine/Source/DataSources/EntityView.js @@ -8,8 +8,11 @@ import JulianDate from "../Core/JulianDate.js"; import CesiumMath from "../Core/Math.js"; import Matrix3 from "../Core/Matrix3.js"; import Matrix4 from "../Core/Matrix4.js"; +import Quaternion from "../Core/Quaternion.js"; +import ReferenceFrame from "../Core/ReferenceFrame.js"; import Transforms from "../Core/Transforms.js"; import SceneMode from "../Scene/SceneMode.js"; +import VelocityOrientationProperty from "./VelocityOrientationProperty.js"; const updateTransformMatrix3Scratch1 = new Matrix3(); const updateTransformMatrix3Scratch2 = new Matrix3(); @@ -21,6 +24,7 @@ const updateTransformCartesian3Scratch3 = new Cartesian3(); const updateTransformCartesian3Scratch4 = new Cartesian3(); const updateTransformCartesian3Scratch5 = new Cartesian3(); const updateTransformCartesian3Scratch6 = new Cartesian3(); +const updateTransformOrientationScratch = new Quaternion(); const deltaTime = new JulianDate(); const northUpAxisFactor = 1.25; // times ellipsoid's maximum radius @@ -30,6 +34,8 @@ function updateTransform( updateLookAt, saveCamera, positionProperty, + orientationProperty, + trackingReferenceFrame, time, ellipsoid, ) { @@ -228,7 +234,23 @@ function updateTransform( } const transform = updateTransformMatrix4Scratch; - if (hasBasis) { + + const orientation = orientationProperty.getValue( + time, + updateTransformOrientationScratch + ); + + if ( + trackingReferenceFrame === ReferenceFrame.INERTIAL && + defined(orientation) + ) { + Matrix4.fromTranslationQuaternionRotationScale( + cartesian, + orientation, + Cartesian3.ONE, + transform + ); + } else if (hasBasis) { transform[0] = xBasis.x; transform[1] = xBasis.y; transform[2] = xBasis.z; @@ -250,13 +272,20 @@ function updateTransform( Transforms.eastNorthUpToFixedFrame(cartesian, ellipsoid, transform); } - camera._setTransform(transform); + if ( + trackingReferenceFrame === ReferenceFrame.FIXED || + mode === SceneMode.SCENE2D + ) { + camera._setTransform(transform); - if (saveCamera) { - Cartesian3.clone(position, camera.position); - Cartesian3.clone(direction, camera.direction); - Cartesian3.clone(up, camera.up); - Cartesian3.cross(direction, up, camera.right); + if (saveCamera) { + Cartesian3.clone(position, camera.position); + Cartesian3.clone(direction, camera.direction); + Cartesian3.clone(up, camera.up); + Cartesian3.cross(direction, up, camera.right); + } + } else { + camera.lookAtTransform(transform, position); } } @@ -316,6 +345,11 @@ function EntityView(entity, scene, ellipsoid) { this._lastCartesian = new Cartesian3(); this._defaultOffset3D = undefined; + this._orientationProperty = new VelocityOrientationProperty( + entity.position, + ellipsoid + ); + this._offset3D = new Cartesian3(); } @@ -362,10 +396,18 @@ EntityView.prototype.update = function (time, boundingSphere) { } const entity = this.entity; + const trackingReferenceFrame = entity.trackingReferenceFrame; const positionProperty = entity.position; if (!defined(positionProperty)) { return; } + const orientationProperty = this._orientationProperty; + if ( + trackingReferenceFrame === ReferenceFrame.INERTIAL && + !defined(orientationProperty) + ) { + return; + } const objectChanged = entity !== this._lastEntity; const sceneModeChanged = sceneMode !== this._mode; @@ -419,6 +461,8 @@ EntityView.prototype.update = function (time, boundingSphere) { updateLookAt, saveCamera, positionProperty, + orientationProperty, + trackingReferenceFrame, time, ellipsoid, ); diff --git a/packages/engine/Specs/DataSources/EntitySpec.js b/packages/engine/Specs/DataSources/EntitySpec.js index 4edfdbffc4da..ed12355c8d4d 100644 --- a/packages/engine/Specs/DataSources/EntitySpec.js +++ b/packages/engine/Specs/DataSources/EntitySpec.js @@ -4,6 +4,7 @@ import { Matrix3, Matrix4, Quaternion, + ReferenceFrame, TimeInterval, TimeIntervalCollection, Transforms, @@ -54,11 +55,13 @@ describe("DataSources/Entity", function () { expect(entity.viewFrom).toBeUndefined(); expect(entity.wall).toBeUndefined(); expect(entity.entityCollection).toBeUndefined(); + expect(entity.trackingReferenceFrame).toBe(ReferenceFrame.FIXED); const options = { id: "someId", name: "bob", show: false, + trackingReferenceFrame: ReferenceFrame.INERTIAL, availability: new TimeIntervalCollection(), parent: new Entity(), customProperty: {}, @@ -88,6 +91,7 @@ describe("DataSources/Entity", function () { expect(entity.id).toEqual(options.id); expect(entity.name).toEqual(options.name); expect(entity.show).toBe(options.show); + expect(entity.trackingReferenceFrame).toBe(options.trackingReferenceFrame); expect(entity.availability).toBe(options.availability); expect(entity.parent).toBe(options.parent); expect(entity.customProperty).toBe(options.customProperty); diff --git a/packages/engine/Specs/DataSources/EntityViewSpec.js b/packages/engine/Specs/DataSources/EntityViewSpec.js index 1406cf8207d3..132ff0702f2f 100644 --- a/packages/engine/Specs/DataSources/EntityViewSpec.js +++ b/packages/engine/Specs/DataSources/EntityViewSpec.js @@ -6,6 +6,7 @@ import { ConstantPositionProperty, Entity, EntityView, + ReferenceFrame, } from "../../index.js"; import createScene from "../../../../Specs/createScene.js"; @@ -74,6 +75,10 @@ describe( const view = new EntityView(entity, scene); view.update(JulianDate.now()); expect(view.scene.camera.position).toEqualEpsilon(sampleOffset, 1e-10); + + entity.trackingReferenceFrame = ReferenceFrame.INERTIAL; + view.update(JulianDate.now()); + expect(view.scene.camera.position).toEqualEpsilon(sampleOffset, 1e-10); }); it("uses entity bounding sphere", function () { @@ -92,6 +97,13 @@ describe( new BoundingSphere(new Cartesian3(3, 4, 5), 6), ); expect(view.scene.camera.position).toEqualEpsilon(sampleOffset, 1e-10); + + entity.trackingReferenceFrame = ReferenceFrame.INERTIAL; + view.update( + JulianDate.now(), + new BoundingSphere(new Cartesian3(3, 4, 5), 6) + ); + expect(view.scene.camera.position).toEqualEpsilon(sampleOffset, 1e-10); }); it("uses entity viewFrom if available and boundingsphere is supplied", function () { @@ -109,6 +121,10 @@ describe( ); view.update(JulianDate.now()); expect(view.scene.camera.position).toEqualEpsilon(sampleOffset, 1e-10); + + entity.trackingReferenceFrame = ReferenceFrame.INERTIAL; + view.update(JulianDate.now()); + expect(view.scene.camera.position).toEqualEpsilon(sampleOffset, 1e-10); }); it("update throws without time parameter", function () { @@ -125,6 +141,13 @@ describe( const view = new EntityView(entity, scene); view.update(JulianDate.now()); }); + + it("update returns with entity.trackingReferenceFrame set to INERTIAL and without entity.orientation property.", function () { + const entity = new Entity(); + entity.trackingReferenceFrame = ReferenceFrame.INERTIAL; + const view = new EntityView(entity, scene); + view.update(JulianDate.now()); + }); }, "WebGL", ); From 916689c6291c0fc24a9c3a1d3fc9e183485ff5e4 Mon Sep 17 00:00:00 2001 From: "jerome.fayot" Date: Sun, 15 Sep 2024 15:35:06 +0200 Subject: [PATCH 044/175] fix: only use VelocityOrientationProperty if the entity doesn't have an orientation defined --- packages/engine/Source/DataSources/EntityView.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/engine/Source/DataSources/EntityView.js b/packages/engine/Source/DataSources/EntityView.js index b33b61e29111..c4769b897cf3 100644 --- a/packages/engine/Source/DataSources/EntityView.js +++ b/packages/engine/Source/DataSources/EntityView.js @@ -345,10 +345,10 @@ function EntityView(entity, scene, ellipsoid) { this._lastCartesian = new Cartesian3(); this._defaultOffset3D = undefined; - this._orientationProperty = new VelocityOrientationProperty( - entity.position, - ellipsoid - ); + this._orientationProperty = + entity.orientation !== undefined + ? entity.orientation + : new VelocityOrientationProperty(entity.position, ellipsoid); this._offset3D = new Cartesian3(); } From 295d6fbc8f1db87c7d239cd76fe00c2d80daa25b Mon Sep 17 00:00:00 2001 From: "jerome.fayot" Date: Mon, 16 Sep 2024 10:08:06 +0200 Subject: [PATCH 045/175] fix: not ready for defaulting to VelocityOrientationProperty yet (see #9159 and #8900) --- packages/engine/Source/DataSources/Entity.js | 2 +- .../engine/Source/DataSources/EntityView.js | 19 ++++++++----------- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/packages/engine/Source/DataSources/Entity.js b/packages/engine/Source/DataSources/Entity.js index ed35b3a13da3..b030d594e801 100644 --- a/packages/engine/Source/DataSources/Entity.js +++ b/packages/engine/Source/DataSources/Entity.js @@ -76,7 +76,7 @@ function createPropertyTypeDescriptor(name, Type) { * @property {boolean} [show] A boolean value indicating if the entity and its children are displayed. * @property {ReferenceFrame} [trackingReferenceFrame=ReferenceFrame.FIXED] The reference frame used when this entity is being tracked. If undefined, east-north-up at entity's position is used. When set to ReferenceFrame.INERTIAL, the camera will track and rotate according to entity's position and orirentation. * @property {Property | string} [description] A string Property specifying an HTML description for this entity. - * @property {PositionProperty | Cartesian3 | CallbackProperty} [position] A Property specifying the entity position. + * @property {PositionProperty | Cartesian3 | CallbackPositionProperty} [position] A Property specifying the entity position. * @property {Property | Quaternion} [orientation=Transforms.eastNorthUpToFixedFrame(position)] A Property specifying the entity orientation in respect to Earth-fixed-Earth-centered (ECEF). If undefined, east-north-up at entity position is used. * @property {Property | Cartesian3} [viewFrom] A suggested initial offset for viewing this object. * @property {Entity} [parent] A parent entity to associate with this entity. diff --git a/packages/engine/Source/DataSources/EntityView.js b/packages/engine/Source/DataSources/EntityView.js index c4769b897cf3..e55d2b0e8d86 100644 --- a/packages/engine/Source/DataSources/EntityView.js +++ b/packages/engine/Source/DataSources/EntityView.js @@ -12,7 +12,6 @@ import Quaternion from "../Core/Quaternion.js"; import ReferenceFrame from "../Core/ReferenceFrame.js"; import Transforms from "../Core/Transforms.js"; import SceneMode from "../Scene/SceneMode.js"; -import VelocityOrientationProperty from "./VelocityOrientationProperty.js"; const updateTransformMatrix3Scratch1 = new Matrix3(); const updateTransformMatrix3Scratch2 = new Matrix3(); @@ -235,10 +234,13 @@ function updateTransform( const transform = updateTransformMatrix4Scratch; - const orientation = orientationProperty.getValue( - time, - updateTransformOrientationScratch - ); + let orientation; + if (defined(orientationProperty)) { + orientation = orientationProperty.getValue( + time, + updateTransformOrientationScratch + ); + } if ( trackingReferenceFrame === ReferenceFrame.INERTIAL && @@ -345,11 +347,6 @@ function EntityView(entity, scene, ellipsoid) { this._lastCartesian = new Cartesian3(); this._defaultOffset3D = undefined; - this._orientationProperty = - entity.orientation !== undefined - ? entity.orientation - : new VelocityOrientationProperty(entity.position, ellipsoid); - this._offset3D = new Cartesian3(); } @@ -401,7 +398,7 @@ EntityView.prototype.update = function (time, boundingSphere) { if (!defined(positionProperty)) { return; } - const orientationProperty = this._orientationProperty; + const orientationProperty = entity.orientation; if ( trackingReferenceFrame === ReferenceFrame.INERTIAL && !defined(orientationProperty) From 78cc8071016ab827119d201dcf73adc211e8e8d7 Mon Sep 17 00:00:00 2001 From: "jerome.fayot" Date: Thu, 19 Sep 2024 16:19:19 +0200 Subject: [PATCH 046/175] fix: created dedicated TrackingReferenceFrame constants not to be confused with ReferenceFrame ones --- .../gallery/Callback Position Property.html | 2 +- Apps/Sandcastle/gallery/Interpolation.html | 8 ++++--- .../Source/Core/TrackingReferenceFrame.js | 23 +++++++++++++++++++ packages/engine/Source/DataSources/Entity.js | 8 +++---- .../engine/Source/DataSources/EntityView.js | 8 +++---- .../engine/Specs/DataSources/EntitySpec.js | 9 +++++--- .../Specs/DataSources/EntityViewSpec.js | 10 ++++---- 7 files changed, 48 insertions(+), 20 deletions(-) create mode 100644 packages/engine/Source/Core/TrackingReferenceFrame.js diff --git a/Apps/Sandcastle/gallery/Callback Position Property.html b/Apps/Sandcastle/gallery/Callback Position Property.html index 6f748d3fe970..4786dfd84822 100644 --- a/Apps/Sandcastle/gallery/Callback Position Property.html +++ b/Apps/Sandcastle/gallery/Callback Position Property.html @@ -158,7 +158,7 @@ leadTime: 1, trailTime: 0.1, }, - trackingReferenceFrame: Cesium.ReferenceFrame.INERTIAL, + trackingReferenceFrame: Cesium.TrackingReferenceFrame.INERTIAL, viewFrom: new Cesium.Cartesian3(-100, 0, 10), }); diff --git a/Apps/Sandcastle/gallery/Interpolation.html b/Apps/Sandcastle/gallery/Interpolation.html index 409ee4485148..0c01e6547df7 100644 --- a/Apps/Sandcastle/gallery/Interpolation.html +++ b/Apps/Sandcastle/gallery/Interpolation.html @@ -156,15 +156,17 @@ Sandcastle.addToolbarMenu([ { - text: "Tracking reference frame: Fixed", + text: "Tracking reference frame: East-North-Up", onselect: function () { - entity.trackingReferenceFrame = Cesium.ReferenceFrame.FIXED; + entity.trackingReferenceFrame = + Cesium.TrackingReferenceFrame.EAST_NORTH_UP; }, }, { text: "Tracking reference frame: Inertial", onselect: function () { - entity.trackingReferenceFrame = Cesium.ReferenceFrame.INERTIAL; + entity.trackingReferenceFrame = + Cesium.TrackingReferenceFrame.INERTIAL; }, }, ]); diff --git a/packages/engine/Source/Core/TrackingReferenceFrame.js b/packages/engine/Source/Core/TrackingReferenceFrame.js new file mode 100644 index 000000000000..29294e828e5e --- /dev/null +++ b/packages/engine/Source/Core/TrackingReferenceFrame.js @@ -0,0 +1,23 @@ +/** + * Constants for identifying well-known tracking reference frames. + * + * @enum {number} + */ +const TrackingReferenceFrame = { + /** + * The east-north-up entity's frame. + * + * @type {number} + * @constant + */ + EAST_NORTH_UP: 0, + + /** + * The entity's inertial frame. + * + * @type {number} + * @constant + */ + INERTIAL: 1, +}; +export default Object.freeze(TrackingReferenceFrame); diff --git a/packages/engine/Source/DataSources/Entity.js b/packages/engine/Source/DataSources/Entity.js index b030d594e801..d0ff82b413df 100644 --- a/packages/engine/Source/DataSources/Entity.js +++ b/packages/engine/Source/DataSources/Entity.js @@ -10,7 +10,7 @@ import CesiumMath from "../Core/Math.js"; import Matrix3 from "../Core/Matrix3.js"; import Matrix4 from "../Core/Matrix4.js"; import Quaternion from "../Core/Quaternion.js"; -import ReferenceFrame from "../Core/ReferenceFrame.js"; +import TrackingReferenceFrame from "../Core/TrackingReferenceFrame.js"; import Transforms from "../Core/Transforms.js"; import GroundPolylinePrimitive from "../Scene/GroundPolylinePrimitive.js"; import GroundPrimitive from "../Scene/GroundPrimitive.js"; @@ -74,7 +74,7 @@ function createPropertyTypeDescriptor(name, Type) { * @property {string} [name] A human readable name to display to users. It does not have to be unique. * @property {TimeIntervalCollection} [availability] The availability, if any, associated with this object. * @property {boolean} [show] A boolean value indicating if the entity and its children are displayed. - * @property {ReferenceFrame} [trackingReferenceFrame=ReferenceFrame.FIXED] The reference frame used when this entity is being tracked. If undefined, east-north-up at entity's position is used. When set to ReferenceFrame.INERTIAL, the camera will track and rotate according to entity's position and orirentation. + * @property {TrackingReferenceFrame} [trackingReferenceFrame=TrackingReferenceFrame.EAST_NORTH_UP] The reference frame used when this entity is being tracked. If undefined, east-north-up at entity's position is used. When set to TrackingReferenceFrame.INERTIAL, the camera will track and rotate according to entity's position and orirentation. * @property {Property | string} [description] A string Property specifying an HTML description for this entity. * @property {PositionProperty | Cartesian3 | CallbackPositionProperty} [position] A Property specifying the entity position. * @property {Property | Quaternion} [orientation=Transforms.eastNorthUpToFixedFrame(position)] A Property specifying the entity orientation in respect to Earth-fixed-Earth-centered (ECEF). If undefined, east-north-up at entity position is used. @@ -126,7 +126,7 @@ function Entity(options) { this._show = defaultValue(options.show, true); this._trackingReferenceFrame = defaultValue( options.trackingReferenceFrame, - ReferenceFrame.FIXED + TrackingReferenceFrame.EAST_NORTH_UP ); this._parent = undefined; this._propertyNames = [ @@ -307,7 +307,7 @@ Object.defineProperties(Entity.prototype, { * @demo {@link https://sandcastle.cesium.com/index.html?src=Interpolation.html|Cesium Sandcastle Interpolation Demo} * * @memberof Entity.prototype - * @type {ReferenceFrame} + * @type {TrackingReferenceFrame} */ trackingReferenceFrame: createRawPropertyDescriptor("trackingReferenceFrame"), /** diff --git a/packages/engine/Source/DataSources/EntityView.js b/packages/engine/Source/DataSources/EntityView.js index e55d2b0e8d86..c4fac8c8a7e2 100644 --- a/packages/engine/Source/DataSources/EntityView.js +++ b/packages/engine/Source/DataSources/EntityView.js @@ -9,7 +9,7 @@ import CesiumMath from "../Core/Math.js"; import Matrix3 from "../Core/Matrix3.js"; import Matrix4 from "../Core/Matrix4.js"; import Quaternion from "../Core/Quaternion.js"; -import ReferenceFrame from "../Core/ReferenceFrame.js"; +import TrackingReferenceFrame from "../Core/TrackingReferenceFrame.js"; import Transforms from "../Core/Transforms.js"; import SceneMode from "../Scene/SceneMode.js"; @@ -243,7 +243,7 @@ function updateTransform( } if ( - trackingReferenceFrame === ReferenceFrame.INERTIAL && + trackingReferenceFrame === TrackingReferenceFrame.INERTIAL && defined(orientation) ) { Matrix4.fromTranslationQuaternionRotationScale( @@ -275,7 +275,7 @@ function updateTransform( } if ( - trackingReferenceFrame === ReferenceFrame.FIXED || + trackingReferenceFrame === TrackingReferenceFrame.EAST_NORTH_UP || mode === SceneMode.SCENE2D ) { camera._setTransform(transform); @@ -400,7 +400,7 @@ EntityView.prototype.update = function (time, boundingSphere) { } const orientationProperty = entity.orientation; if ( - trackingReferenceFrame === ReferenceFrame.INERTIAL && + trackingReferenceFrame === TrackingReferenceFrame.INERTIAL && !defined(orientationProperty) ) { return; diff --git a/packages/engine/Specs/DataSources/EntitySpec.js b/packages/engine/Specs/DataSources/EntitySpec.js index ed12355c8d4d..3f3c87728625 100644 --- a/packages/engine/Specs/DataSources/EntitySpec.js +++ b/packages/engine/Specs/DataSources/EntitySpec.js @@ -4,7 +4,7 @@ import { Matrix3, Matrix4, Quaternion, - ReferenceFrame, + TrackingReferenceFrame, TimeInterval, TimeIntervalCollection, Transforms, @@ -55,13 +55,15 @@ describe("DataSources/Entity", function () { expect(entity.viewFrom).toBeUndefined(); expect(entity.wall).toBeUndefined(); expect(entity.entityCollection).toBeUndefined(); - expect(entity.trackingReferenceFrame).toBe(ReferenceFrame.FIXED); + expect(entity.trackingReferenceFrame).toBe( + TrackingReferenceFrame.EAST_NORTH_UP + ); const options = { id: "someId", name: "bob", show: false, - trackingReferenceFrame: ReferenceFrame.INERTIAL, + trackingReferenceFrame: TrackingReferenceFrame.INERTIAL, availability: new TimeIntervalCollection(), parent: new Entity(), customProperty: {}, @@ -118,6 +120,7 @@ describe("DataSources/Entity", function () { expect(entity.wall).toBeInstanceOf(WallGraphics); expect(entity.entityCollection).toBeUndefined(); + expect(entity.trackingReferenceFrame).toBe(TrackingReferenceFrame.INERTIAL); }); it("isAvailable is always true if no availability defined.", function () { diff --git a/packages/engine/Specs/DataSources/EntityViewSpec.js b/packages/engine/Specs/DataSources/EntityViewSpec.js index 132ff0702f2f..17ea37b0221f 100644 --- a/packages/engine/Specs/DataSources/EntityViewSpec.js +++ b/packages/engine/Specs/DataSources/EntityViewSpec.js @@ -6,7 +6,7 @@ import { ConstantPositionProperty, Entity, EntityView, - ReferenceFrame, + TrackingReferenceFrame, } from "../../index.js"; import createScene from "../../../../Specs/createScene.js"; @@ -76,7 +76,7 @@ describe( view.update(JulianDate.now()); expect(view.scene.camera.position).toEqualEpsilon(sampleOffset, 1e-10); - entity.trackingReferenceFrame = ReferenceFrame.INERTIAL; + entity.trackingReferenceFrame = TrackingReferenceFrame.INERTIAL; view.update(JulianDate.now()); expect(view.scene.camera.position).toEqualEpsilon(sampleOffset, 1e-10); }); @@ -98,7 +98,7 @@ describe( ); expect(view.scene.camera.position).toEqualEpsilon(sampleOffset, 1e-10); - entity.trackingReferenceFrame = ReferenceFrame.INERTIAL; + entity.trackingReferenceFrame = TrackingReferenceFrame.INERTIAL; view.update( JulianDate.now(), new BoundingSphere(new Cartesian3(3, 4, 5), 6) @@ -122,7 +122,7 @@ describe( view.update(JulianDate.now()); expect(view.scene.camera.position).toEqualEpsilon(sampleOffset, 1e-10); - entity.trackingReferenceFrame = ReferenceFrame.INERTIAL; + entity.trackingReferenceFrame = TrackingReferenceFrame.INERTIAL; view.update(JulianDate.now()); expect(view.scene.camera.position).toEqualEpsilon(sampleOffset, 1e-10); }); @@ -144,7 +144,7 @@ describe( it("update returns with entity.trackingReferenceFrame set to INERTIAL and without entity.orientation property.", function () { const entity = new Entity(); - entity.trackingReferenceFrame = ReferenceFrame.INERTIAL; + entity.trackingReferenceFrame = TrackingReferenceFrame.INERTIAL; const view = new EntityView(entity, scene); view.update(JulianDate.now()); }); From 917adb20d8036abab0bc1e6efb5dc1030df82e72 Mon Sep 17 00:00:00 2001 From: "jerome.fayot" Date: Tue, 24 Sep 2024 11:41:47 +0200 Subject: [PATCH 047/175] doc: added entity tracking sandcastle demo --- Apps/SampleData/tracking.czml | 4010 +++++++++++++++++ Apps/Sandcastle/gallery/Entity tracking.html | 123 + Apps/Sandcastle/gallery/Entity tracking.jpg | Bin 0 -> 16430 bytes .../Source/Core/TrackingReferenceFrame.js | 23 +- packages/engine/Source/DataSources/Entity.js | 9 +- .../engine/Source/DataSources/EntityView.js | 45 +- .../engine/Specs/DataSources/EntitySpec.js | 2 +- .../Specs/DataSources/EntityViewSpec.js | 15 + 8 files changed, 4205 insertions(+), 22 deletions(-) create mode 100644 Apps/SampleData/tracking.czml create mode 100644 Apps/Sandcastle/gallery/Entity tracking.html create mode 100644 Apps/Sandcastle/gallery/Entity tracking.jpg diff --git a/Apps/SampleData/tracking.czml b/Apps/SampleData/tracking.czml new file mode 100644 index 000000000000..11fff8a3f146 --- /dev/null +++ b/Apps/SampleData/tracking.czml @@ -0,0 +1,4010 @@ + +[ + { + "id":"document", + "name":"tracking", + "version":"1.0", + "clock":{ + "interval":"2012-03-15T10:00:00Z/2012-03-16T10:00:00Z", + "currentTime":"2012-03-15T10:00:00Z", + "range":"LOOP_STOP" + } + }, + { + "id":"Satellite/ISS", + "name":"ISS", + "availability":"2012-03-15T10:00:00Z/2012-03-16T10:00:00Z", + "description":"\r\n

The International Space Station (ISS) is a space station, or a habitable artificial satellite in low Earth orbit. It is a modular structure whose first component was launched in 1998. Now the largest artificial body in orbit, it can often be seen at the appropriate time with the naked eye from Earth. The ISS consists of pressurised modules, external trusses, solar arrays and other components. ISS components have been launched by American Space Shuttles as well as Russian Proton and Soyuz rockets. In 1984 the ESA was invited to participate in Space Station Freedom. In 1993, after the USSR ended, the United States and Russia merged Mir-2 and Freedom together.\r\nThe ISS serves as a microgravity and space environment research laboratory in which crew members conduct experiments in biology, human biology, physics, astronomy, meteorology and other fields. The station is suited for the testing of spacecraft systems and equipment required for missions to the Moon and Mars.

\r\n\r\n

Since the arrival of Expedition 1 on 2 November 2000, the station has been continuously occupied for 13 years and 86 days, the longest continuous human presence in space. (In 2010, the station surpassed the previous record of almost 10 years (or 3,634 days) held by Mir.) The station is serviced by a variety of visiting spacecraft: Soyuz, Progress, the Automated Transfer Vehicle, the H-II Transfer Vehicle, Dragon, and Cygnus. It has been visited by astronauts and cosmonauts from 15 different nations.

\r\n\r\n

After the U.S. Space Shuttle program ended in 2011, Soyuz rockets became the only provider of transport for astronauts at the International Space Station.\r\nThe ISS programme is a joint project among five participating space agencies: NASA, Roskosmos, JAXA, ESA, and CSA. The ownership and use of the space station is established by intergovernmental treaties and agreements. The station is divided into two sections, the Russian Orbital Segment (ROS) and the United States Orbital Segment (USOS), which is shared by many nations. The ISS maintains an orbit with an altitude of between 330 km (205 mi) and 435 km (270 mi) by means of reboost manoeuvres using the engines of the Zvezda module or visiting spacecraft. It completes 15.410 orbits per day. The ISS is funded until 2024, and may operate until 2028. The Russian Federal Space Agency, Roskosmos (RKA) has proposed using the ISS to commission modules for a new space station, called OPSEK, before the remainder of the ISS is deorbited. ISS is the ninth space station to be inhabited by crews, following the Soviet and later Russian Salyut, Almaz, and Mir stations, and Skylab from the US.

", + "billboard":{ + "eyeOffset":{ + "cartesian":[ + 0,0,0 + ] + }, + "horizontalOrigin":"CENTER", + "image":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADJSURBVDhPnZHRDcMgEEMZjVEYpaNklIzSEfLfD4qNnXAJSFWfhO7w2Zc0Tf9QG2rXrEzSUeZLOGm47WoH95x3Hl3jEgilvDgsOQUTqsNl68ezEwn1vae6lceSEEYvvWNT/Rxc4CXQNGadho1NXoJ+9iaqc2xi2xbt23PJCDIB6TQjOC6Bho/sDy3fBQT8PrVhibU7yBFcEPaRxOoeTwbwByCOYf9VGp1BYI1BA+EeHhmfzKbBoJEQwn1yzUZtyspIQUha85MpkNIXB7GizqDEECsAAAAASUVORK5CYII=", + "pixelOffset":{ + "cartesian2":[ + 0,0 + ] + }, + "scale":1.5, + "show":true, + "verticalOrigin":"CENTER" + }, + "label":{ + "fillColor":{ + "rgba":[ + 255,0,255,255 + ] + }, + "font":"11pt Lucida Console", + "horizontalOrigin":"LEFT", + "outlineColor":{ + "rgba":[ + 0,0,0,255 + ] + }, + "outlineWidth":2, + "pixelOffset":{ + "cartesian2":[ + 12,0 + ] + }, + "show":true, + "style":"FILL_AND_OUTLINE", + "text":"ISS", + "verticalOrigin":"CENTER" + }, + "path":{ + "show":[ + { + "interval":"2012-03-15T10:00:00Z/2012-03-16T10:00:00Z", + "boolean":true + } + ], + "width":1, + "material":{ + "solidColor":{ + "color":{ + "rgba":[ + 255,0,255,255 + ] + } + } + }, + "resolution":120, + "leadTime":[ + { + "interval":"2012-03-15T10:00:00Z/2012-03-15T10:44:56.1031157730031Z", + "epoch":"2012-03-15T10:00:00Z", + "number":[ + 0,5537.546684141998, + 5537.546684141998,0 + ] + }, + { + "interval":"2012-03-15T10:44:56.1031157730031Z/2012-03-15T12:17:13.6497999150015Z", + "epoch":"2012-03-15T10:44:56.1031157730031Z", + "number":[ + 0,5537.546684141998, + 5537.546684141998,0 + ] + }, + { + "interval":"2012-03-15T12:17:13.6497999150015Z/2012-03-15T13:49:31.2088613029919Z", + "epoch":"2012-03-15T12:17:13.6497999150015Z", + "number":[ + 0,5537.55906138799, + 5537.55906138799,0 + ] + }, + { + "interval":"2012-03-15T13:49:31.2088613029919Z/2012-03-15T15:21:48.776005533Z", + "epoch":"2012-03-15T13:49:31.2088613029919Z", + "number":[ + 0,5537.567144230008, + 5537.567144230008,0 + ] + }, + { + "interval":"2012-03-15T15:21:48.776005533Z/2012-03-15T16:54:06.33371177400113Z", + "epoch":"2012-03-15T15:21:48.776005533Z", + "number":[ + 0,5537.557706241001, + 5537.557706241001,0 + ] + }, + { + "interval":"2012-03-15T16:54:06.33371177400113Z/2012-03-15T18:26:23.8819795500021Z", + "epoch":"2012-03-15T16:54:06.33371177400113Z", + "number":[ + 0,5537.548267776001, + 5537.548267776001,0 + ] + }, + { + "interval":"2012-03-15T18:26:23.8819795500021Z/2012-03-15T19:58:41.3312553199939Z", + "epoch":"2012-03-15T18:26:23.8819795500021Z", + "number":[ + 0,5537.449275769992, + 5537.449275769992,0 + ] + }, + { + "interval":"2012-03-15T19:58:41.3312553199939Z/2012-03-15T21:30:58.8527762320009Z", + "epoch":"2012-03-15T19:58:41.3312553199939Z", + "number":[ + 0,5537.521520912007, + 5537.521520912007,0 + ] + }, + { + "interval":"2012-03-15T21:30:58.8527762320009Z/2012-03-15T23:03:16.3758652800025Z", + "epoch":"2012-03-15T21:30:58.8527762320009Z", + "number":[ + 0,5537.523089048002, + 5537.523089048002,0 + ] + }, + { + "interval":"2012-03-15T23:03:16.3758652800025Z/2012-03-16T00:35:33.8758586170152Z", + "epoch":"2012-03-15T23:03:16.3758652800025Z", + "number":[ + 0,5537.499993337013, + 5537.499993337013,0 + ] + }, + { + "interval":"2012-03-16T00:35:33.8758586170152Z/2012-03-16T02:07:51.3639393709891Z", + "epoch":"2012-03-16T00:35:33.8758586170152Z", + "number":[ + 0,5537.488080753974, + 5537.488080753974,0 + ] + }, + { + "interval":"2012-03-16T02:07:51.3639393709891Z/2012-03-16T03:40:08.84424079000019Z", + "epoch":"2012-03-16T02:07:51.3639393709891Z", + "number":[ + 0,5537.480301419011, + 5537.480301419011,0 + ] + }, + { + "interval":"2012-03-16T03:40:08.84424079000019Z/2012-03-16T05:12:26.2685025699902Z", + "epoch":"2012-03-16T03:40:08.84424079000019Z", + "number":[ + 0,5537.42426177999, + 5537.42426177999,0 + ] + }, + { + "interval":"2012-03-16T05:12:26.2685025699902Z/2012-03-16T06:44:43.7279617500026Z", + "epoch":"2012-03-16T05:12:26.2685025699902Z", + "number":[ + 0,5537.459459180012, + 5537.459459180012,0 + ] + }, + { + "interval":"2012-03-16T06:44:43.7279617500026Z/2012-03-16T08:17:01.17765616998076Z", + "epoch":"2012-03-16T06:44:43.7279617500026Z", + "number":[ + 0,5537.449694419978, + 5537.449694419978,0 + ] + }, + { + "interval":"2012-03-16T08:17:01.17765616998076Z/2012-03-16T08:27:42.5600708879647Z", + "epoch":"2012-03-16T08:17:01.17765616998076Z", + "number":[ + 0,5537.439929112035, + 5537.439929112035,0 + ] + }, + { + "interval":"2012-03-16T08:27:42.5600708879647Z/2012-03-16T10:00:00Z", + "epoch":"2012-03-16T08:27:42.5600708879647Z", + "number":[ + 0,5537.439929112035, + 5537.439929112035,0 + ] + } + ], + "trailTime":[ + { + "interval":"2012-03-15T10:00:00Z/2012-03-15T10:44:56.1031157730031Z", + "epoch":"2012-03-15T10:00:00Z", + "number":[ + 0,0, + 5537.546684141998,5537.546684141998 + ] + }, + { + "interval":"2012-03-15T10:44:56.1031157730031Z/2012-03-15T12:17:13.6497999150015Z", + "epoch":"2012-03-15T10:44:56.1031157730031Z", + "number":[ + 0,0, + 5537.546684141998,5537.546684141998 + ] + }, + { + "interval":"2012-03-15T12:17:13.6497999150015Z/2012-03-15T13:49:31.2088613029919Z", + "epoch":"2012-03-15T12:17:13.6497999150015Z", + "number":[ + 0,0, + 5537.55906138799,5537.55906138799 + ] + }, + { + "interval":"2012-03-15T13:49:31.2088613029919Z/2012-03-15T15:21:48.776005533Z", + "epoch":"2012-03-15T13:49:31.2088613029919Z", + "number":[ + 0,0, + 5537.567144230008,5537.567144230008 + ] + }, + { + "interval":"2012-03-15T15:21:48.776005533Z/2012-03-15T16:54:06.33371177400113Z", + "epoch":"2012-03-15T15:21:48.776005533Z", + "number":[ + 0,0, + 5537.557706241001,5537.557706241001 + ] + }, + { + "interval":"2012-03-15T16:54:06.33371177400113Z/2012-03-15T18:26:23.8819795500021Z", + "epoch":"2012-03-15T16:54:06.33371177400113Z", + "number":[ + 0,0, + 5537.548267776001,5537.548267776001 + ] + }, + { + "interval":"2012-03-15T18:26:23.8819795500021Z/2012-03-15T19:58:41.3312553199939Z", + "epoch":"2012-03-15T18:26:23.8819795500021Z", + "number":[ + 0,0, + 5537.449275769992,5537.449275769992 + ] + }, + { + "interval":"2012-03-15T19:58:41.3312553199939Z/2012-03-15T21:30:58.8527762320009Z", + "epoch":"2012-03-15T19:58:41.3312553199939Z", + "number":[ + 0,0, + 5537.521520912007,5537.521520912007 + ] + }, + { + "interval":"2012-03-15T21:30:58.8527762320009Z/2012-03-15T23:03:16.3758652800025Z", + "epoch":"2012-03-15T21:30:58.8527762320009Z", + "number":[ + 0,0, + 5537.523089048002,5537.523089048002 + ] + }, + { + "interval":"2012-03-15T23:03:16.3758652800025Z/2012-03-16T00:35:33.8758586170152Z", + "epoch":"2012-03-15T23:03:16.3758652800025Z", + "number":[ + 0,0, + 5537.499993337013,5537.499993337013 + ] + }, + { + "interval":"2012-03-16T00:35:33.8758586170152Z/2012-03-16T02:07:51.3639393709891Z", + "epoch":"2012-03-16T00:35:33.8758586170152Z", + "number":[ + 0,0, + 5537.488080753974,5537.488080753974 + ] + }, + { + "interval":"2012-03-16T02:07:51.3639393709891Z/2012-03-16T03:40:08.84424079000019Z", + "epoch":"2012-03-16T02:07:51.3639393709891Z", + "number":[ + 0,0, + 5537.480301419011,5537.480301419011 + ] + }, + { + "interval":"2012-03-16T03:40:08.84424079000019Z/2012-03-16T05:12:26.2685025699902Z", + "epoch":"2012-03-16T03:40:08.84424079000019Z", + "number":[ + 0,0, + 5537.42426177999,5537.42426177999 + ] + }, + { + "interval":"2012-03-16T05:12:26.2685025699902Z/2012-03-16T06:44:43.7279617500026Z", + "epoch":"2012-03-16T05:12:26.2685025699902Z", + "number":[ + 0,0, + 5537.459459180012,5537.459459180012 + ] + }, + { + "interval":"2012-03-16T06:44:43.7279617500026Z/2012-03-16T08:17:01.17765616998076Z", + "epoch":"2012-03-16T06:44:43.7279617500026Z", + "number":[ + 0,0, + 5537.449694419978,5537.449694419978 + ] + }, + { + "interval":"2012-03-16T08:17:01.17765616998076Z/2012-03-16T08:27:42.5600708879647Z", + "epoch":"2012-03-16T08:17:01.17765616998076Z", + "number":[ + 0,0, + 5537.439929112035,5537.439929112035 + ] + }, + { + "interval":"2012-03-16T08:27:42.5600708879647Z/2012-03-16T10:00:00Z", + "epoch":"2012-03-16T08:27:42.5600708879647Z", + "number":[ + 0,0, + 5537.439929112035,5537.439929112035 + ] + } + ] + }, + "position":{ + "interpolationAlgorithm":"LAGRANGE", + "interpolationDegree":5, + "referenceFrame":"INERTIAL", + "epoch":"2012-03-15T10:00:00Z", + "cartesian":[ + 0,3849424.41859634,5535808.90838488,-469609.955032837, + 300,2403397.25163735,5923118.10887596,-2208538.08732886, + 600,681059.355577534,5629683.78882362,-3693007.85438243, + 900,-1119416.00777462,4691252.20079689,-4753268.77825646, + 1200,-2792163.54303408,3217045.3927905,-5269236.9636378, + 1500,-4146946.26695958,1376355.11151994,-5183333.77414211, + 1800,-5030073.80001301,-620907.843850796,-4506185.20959366, + 2100,-5341141.73000766,-2547536.97322381,-3315102.27876197, + 2400,-5044213.41576254,-4184126.46337638,-1745607.20300478, + 2700,-4172245.63939562,-5343667.09365887,23409.7618660682, + 3e3,-2823903.53321619,-5893019.5858321,1789694.91494771, + 3300,-1152706.81297821,-5768551.08921845,3350719.50403451, + 3494.253024000005,12647.226754864,-5331683.91916414,4165940.89199221, + 3494.253024000005,13029.0479309608,-5331460.99207657,4166115.22224224, + 3600,650789.93624267,-4983501.3435969,4527207.45547157, + 3900,2379305.75017934,-3627537.39864893,5183179.25792976, + 4200,3834777.21946002,-1855400.2986254,5242729.69023172, + 4500,4849528.23059951,129890.673107136,4698090.47721749, + 4800,5306015.3961238,2100167.56572551,3611124.18995888, + 5100,5150830.60792791,3828302.31094784,2106711.608498, + 5400,4401482.39688381,5114829.18540188,358590.197694369, + 5700,3144674.02408614,5811702.89808911,-1431035.79418686, + 6e3,1525939.53461617,5839838.69437877,-3055607.33495769, + 6300,-267980.182214884,5197915.25437212,-4328682.71628229, + 6600,-2031365.29937242,3961512.87223799,-5105391.73521875, + 6900,-3563213.2763466,2273344.0489862,-5298337.5762072, + 7200,-4689612.83519006,326428.771682565,-4886617.47645177, + 7500,-5282712.94306229,-1657607.93044914,-3917600.03751163, + 7800,-5274754.21237056,-3453007.99287842,-2501594.33092912, + 8100,-4665873.02143146,-4854971.98019869,-799823.217818969, + 8400,-3524604.66138732,-5702910.41801334,993421.369702083, + 8700,-1980586.46565429,-5899120.57098636,2672770.77875102, + 9e3,-209990.19226424,-5420375.16312849,4045403.0406125, + 9300,1584620.06155972,-4320812.37191124,4953336.17627952, + 9600,3197482.28308901,-2725830.9633234,5291644.93513457, + 9868.731839999993,4335118.12525452,-1025094.03550352,5076957.01479314, + 9868.731839999993,4335018.61132235,-1025343.9674375,5077035.18396777, + 9900,4443038.65402268,-818152.432440461,5020664.08503109, + 10200,5177638.17976653,1183789.15226985,4170531.37630724, + 10500,5315710.52847893,3049293.68665747,2838570.0655117, + 10800,4840686.89080989,4563082.50771604,1178298.7746302, + 11100,3807316.87231764,5550567.89507109,-618291.216385065, + 11400,2335314.9664624,5898676.67948854,-2343522.82794638, + 11700,594874.272254555,5568974.91052142,-3798788.25566978, + 1.2e4,-1213816.81950858,4601359.80120866,-4817778.43040243, + 12300,-2884021.70130156,3108323.14089662,-5285177.51260314, + 12600,-4225824.88397817,1261219.92433772,-5148934.77411409, + 12900,-5086993.72268719,-729358.459941565,-4425364.0696591, + 13200,-5369574.39231539,-2636958.95100008,-3197030.8918885, + 13500,-5040837.5197212,-4244298.60262776,-1603714.85512284, + 13800,-4137366.34626142,-5367663.54403821,172926.470930942, + 14100,-2761453.19106198,-5878037.05106202,1929714.21010032, + 14400,-1069805.86733299,-5716261.93867886,3465173.51384947, + 14700,744271.014577194,-4900133.10733316,4602797.70766243, + 1.5e4,2473015.24637326,-3522493.14212366,5211428.32266375, + 15300,3917949.69750226,-1740717.11167563,5220362.57351654, + 15600,4912565.91990974,241050.018704089,4627633.87425256, + 15900,5341597.95913848,2195004.16751206,3500652.18680438, + 16200,5154794.33140615,3895859.5271688,1968954.32783606, + 16500,4373342.05884918,5147295.39445405,209470.919840542, + 16800,3087689.85826338,5805357.43044991,-1574278.60237672, + 17100,1446707.2746503,5795488.70465371,-3176466.52098232, + 17400,-360344.060295147,5120757.79185039,-4413311.43252235, + 17700,-2126304.75226283,3860467.52125647,-5144165.18525407, + 1.8e4,-3649929.17406843,2159984.45316722,-5286887.516213, + 18300,-4758250.11234169,213666.927990528,-4826271.90544669, + 18600,-5325442.2368479,-1756947.10457405,-3815218.86999272, + 18900,-5286657.22396492,-3527602.31698614,-2368820.19001619, + 19200,-4645531.30247392,-4896282.29010561,-651797.787645176, + 19500,-3474297.5624219,-5706180.57236849,1139766.03637674, + 19800,-1906047.93477498,-5863954.29703593,2800659.87029817, + 20100,-119747.901651559,-5350794.90309783,4140162.20596163, + 20400,1680229.19009525,-4224790.44649401,5004082.8628856, + 20700,3287489.2688369,-2614368.27497782,5292531.03251418, + 2.1e4,4517178.30183363,-703777.87584566,4971465.55836947, + 21300,5227207.6648385,1287704.84490596,4076902.01656837, + 21600,5334936.82012406,3130764.60758613,2711278.57345959, + 21900,4827322.58105028,4612697.10330622,1032051.24412722, + 22200,3762924.29010265,5562626.91720275,-766576.689609238, + 22500,2265064.29180843,5871878.64924006,-2476730.65325254, + 22800,506889.553310488,5506538.13251038,-3901627.75375881, + 23100,-1309445.00056429,4510579.15516774,-4878537.15731657, + 23400,-2976402.0280992,2999662.91419282,-5297002.64945855, + 23700,-4304470.18896784,1147095.47806632,-5110539.63058255, + 2.4e4,-5142960.39846207,-835961.695152393,-4341133.17124268, + 24300,-5396449.98957973,-2723906.04437019,-3076534.5475795, + 24600,-5035483.62935387,-4301650.42679393,-1460666.86361877, + 24900,-4100316.26536105,-5388815.78416569,322191.929261417, + 25200,-2696886.30431052,-5860507.97233297,2068100.06234624, + 25500,-985081.652542641,-5662006.20709315,3576793.69154155, + 25800,839459.79250942,-4815326.71580347,4674816.27413303, + 26100,2567753.67880419,-3416822.63632586,5235537.19861624, + 26400,4001341.22224643,-1626275.18017047,5193737.27589445, + 26700,4974974.97424557,351131.478884937,4553277.35585279, + 2.7e4,5375773.87995422,2288048.26911964,3387088.9792563, + 27300,5156748.02950663,3961120.50809531,1829282.25467229, + 27600,4342845.19099752,5177245.67392912,59844.5828064916, + 27900,3028302.49713101,5796600.98139539,-1716563.33131291, + 28200,1365322.17124008,5749152.32120273,-3295029.47245872, + 28500,-454356.746329476,5042305.03137088,-4494597.38838164, + 28800,-2222206.15141325,3758992.7975105,-5178961.77341337, + 29100,-3736821.45847435,2047123.49588175,-5271303.16981529, + 29400,-4826265.95502002,102278.864630022,-4762117.1900019, + 29700,-5366821.81471981,-1854191.86229829,-3709788.89699013, + 3e4,-5296624.22229263,-3599613.59225076,-2234100.76820595, + 30300,-4622868.06480404,-4934799.73886552,-503148.170247627, + 30600,-3421525.18024483,-5706740.4426866,1285341.92829746, + 30900,-1829161.56261067,-5826434.6682239,2926465.90021911, + 31200,-27531.7144464045,-5279448.44528641,4231743.23526868, + 31500,1777212.57374346,-4127757.02234495,5050893.77551254, + 31800,3378103.15780159,-2502741.69029747,5289147.73077336, + 32100,4590977.0373709,-590348.657122951,4918217.20823007, + 32400,5275639.5121592,1389896.8820238,3979865.19308717, + 32517.176831999997,5380098.10478572,2132117.88500271,3480583.59052128, + 32517.176831999997,5380206.78578959,2132704.3566004,3480214.1472366, + 32700,5352360.16433155,3210451.03192908,2581202.47700398, + 3.3e4,4811544.23241305,4660070.40187028,884306.337985066, + 33300,3715810.92574891,5572272.00925959,-914895.68071231, + 33600,2192116.57709469,5842799.91924153,-2608513.17260553, + 33900,416546.428105066,5442236.06083956,-4001762.78504694, + 34200,-1406826.18080549,4418569.09511053,-4935635.03993958, + 34500,-3069733.06488903,2890544.87472284,-5304630.80211245, + 34800,-4383150.76083569,1033328.47975256,-5067879.38290912, + 35100,-5198026.54631352,-941436.515002387,-4253036.88764016, + 35400,-5421566.35735171,-2809075.63112747,-2952995.28558899, + 35700,-5027683.84810168,-4356761.26153088,-1315734.4678681, + 3.6e4,-4060387.62186786,-5407495.6825644,471970.041286316, + 36300,-2629319.40278933,-5840532.63387188,2205562.40102441, + 36600,-897570.045732632,-5605588.3371927,3686149.6135249, + 36900,936905.974602788,-4728874.88306931,4743478.4381812, + 37200,2663958.50480543,-3310205.62360817,5255575.29216226, + 37500,4085238.3175096,-1511695.74486031,5162794.69942285, + 37800,5036875.39765442,460520.818091434,4474863.60855029, + 38100,5408500.33456106,2379649.35813026,3270213.33641568, + 38400,5156502.71194835,4024363.44431024,1687442.37949306, + 38700,4309680.88869103,5204857.1289682,-90545.1306030105, + 3.9e4,2966108.87124662,5785482.51478768,-1858122.30706417, + 39300,1281325.49457454,5700727.20671709,-3411474.84408673, + 39600,-550487.037507769,4962289.69345522,-4572632.48528492, + 39900,-2319493.54179972,3656660.3548582,-5209753.83240227, + 40200,-3824209.81967054,1934199.65577177,-5251412.62186567, + 40500,-4893820.53403162,-8376.93057750767,-4693829.85242935, + 40800,-5406813.43654749,-1949991.09651749,-3600851.87830257, + 41100,-5304406.66726997,-3669618.40613288,-2096885.03389831, + 41400,-4597445.30923729,-4970957.56266923,-353291.724895338, + 41700,-3365711.80596329,-5704831.98690261,1430694.68133986, + 4.2e4,-1749287.19006089,-5786596.35823912,3050635.62042371, + 42300,67282.5104014716,-5206179.73592197,4320450.99966826, + 42600,1876105.53012849,-4029407.41454288,5093914.31300927, + 42900,3469716.53426206,-2390553.98308505,5281488.26936128, + 43200,4664751.44925217,-477181.808910395,4860703.76300645, + 43500,5323035.79623726,1491029.49886786,3879079.76320608, + 43589.759328,5402440.40004548,2056528.00645199,3491627.46684942, + 43589.759328,5402442.54264444,2056738.81686484,3491501.88840014, + 43800,5367840.30507371,3287565.24128733,2449044.98994153, + 44100,4793466.89948304,4704669.22750442,735845.196386836, + 44400,3666300.68694606,5579254.94649742,-1062487.36725239, + 44700,2116947.48184928,5811477.8903838,-2738215.22897224, + 4.5e4,324410.396313805,5376371.6825418,-4098714.1870081, + 45300,-1505371.25160745,4325863.56124646,-4988825.53074582, + 45600,-3163474.68055926,2781685.31685159,-5308091.27375613, + 45900,-4461454.46450387,920751.44587914,-5021285.15291004, + 46200,-5251987.83068379,-1044921.21948849,-4161708.55588854, + 46500,-5444995.16922548,-2891682.7271746,-2827313.90974466, + 46800,-5017826.0163413,-4409036.76825887,-1170010.47688321, + 47100,-4018281.61998877,-5423402.43984196,621088.759416269, + 47400,-2559714.28446385,-5818173.19291614,2340985.6034906, + 47700,-808395.704630906,-5547450.9983423,3792317.64641595, + 4.8e4,1035447.9983595,-4641558.91633002,4808162.62725822, + 48300,2760561.62652593,-3203667.07367331,5271287.27659829, + 48600,4168777.2937882,-1398117.80362934,5127654.23537955, + 48900,5097682.07585487,568104.375379669,4392842.08078676, + 49200,5439501.08861285,2468843.96703679,3150719.8955131, + 49500,5154080.72531675,4084868.12117622,1544267.87631319, + 49800,4274129.47939295,5229707.06682862,-240836.86870874, + 50100,2901588.51435516,5771894.9813108,-1998167.08917933, + 50400,1195328.12796693,5650411.99973262,-3525172.89029405, + 50700,-648065.863983004,4881185.95588798,-4647015.40064705, + 5.1e4,-2417518.44559387,3554173.1319714,-5236422.66927976, + 51300,-3911551.304888,1822079.84885746,-5227416.31542318, + 51600,-4960563.96410311,-117355.616655456,-4621941.65755199, + 51900,-5445340.36795493,-2043429.44720356,-3489249.35182206, + 52200,-5310257.97873623,-3736852.07785834,-1958261.33112716, + 52500,-4569863.84912929,-5004260.18152329,-203458.693372594, + 52800,-3307771.80391093,-5700321.82377175,1574588.75070263, + 53100,-1667567.39473325,-5744714.10305482,3172075.82670534, + 53400,163448.429505371,-5131656.78603713,4405467.10195982, + 53700,1975699.17700698,-3930727.12204608,5132693.71663132, + 5.4e4,3561290.10873786,-2278985.1022354,5269506.88509915, + 54148.15468800001,4203184.07952978,-1353489.53750881,5111445.70468465, + 54148.15468800001,4203191.30262867,-1353475.88589412,5111442.33629379, + 54300,4737737.78046698,-365490.542263622,4799257.56458761, + 54600,5368954.89050152,1589986.17944729,3775186.07047606, + 54900,5381374.56347384,3362167.42325737,2314799.09999447, + 55200,4773053.93352827,4746514.12880929,586645.025608239, + 55500,3614326.93479997,5583552.91481526,-1209386.38537695, + 55800,2039459.06568354,5777834.14557876,-2865875.35129307, + 56100,230353.889530254,5308792.26549321,-4192506.0039312, + 56400,-1605224.578801,4232218.88985275,-5038088.76435784, + 56700,-3257760.70626935,2672746.16024074,-5307286.16717191, + 5.7e4,-4539466.63964283,808949.683395425,-4970553.24732222, + 57300,-5304842.68965046,-1146866.91911664,-4066828.54766123, + 57600,-5466621.8198026,-2972159.5559343,-2699069.09611406, + 57900,-5005678.8581936,-4458833.52479963,-1023009.64607344, + 58200,-3973672.67017912,-5436774.1321751,770044.593415393, + 58500,-2487694.32678156,-5793531.7276185,2474823.52441331, + 58800,-717183.268044322,-5487571.46315032,3895667.50970416, + 59100,1135412.94462176,-4553265.36963682,4869134.29479731, + 59400,2857811.44872052,-3097047.54964928,5282836.82355544, + 59700,4252118.60571663,-1285377.68188911,5088396.78826617, + 6e4,5157476.03077594,674020.929380886,4307236.20275876, + 60300,5468795.1088992,2555731.77859298,3028596.47954634, + 60600,5149455.18246455,4142690.87191001,1399724.51609549, + 60900,4236126.95591789,5251805.34781038,-391079.885552966, + 61200,2834641.93086904,5755792.45957505,-2136754.611779, + 61500,1107196.05353689,5598088.16121645,-3636173.0202672, + 61800,-747252.788966703,4798784.15922154,-4717761.53669296, + 62100,-2516443.11881422,3451220.61614222,-5258915.3231165, + 62400,-3998974.76917799,1710361.73025264,-5199160.16474836, + 62700,-5026550.13601721,-225119.029858565,-4546178.07138944, + 6.3e4,-5482346.83102871,-2134976.21105312,-3374589.79519268, + 63300,-5313997.06814827,-3801730.99838127,-1817750.19592382, + 63600,-4539827.85846879,-5035017.86954231,-53130.9301591509, + 63900,-3247330.33885153,-5693382.22989429,1717523.54805407, + 64200,-1583599.98060968,-5700819.30944626,3291216.20615931, + 64500,261342.090524228,-5055794.75166179,4487118.85260636, + 64800,2076301.68267285,-3831557.43126966,5167447.81526317, + 65100,3653042.56435939,-2167848.84674734,5253319.62533565, + 65400,4810049.038801,-255142.390547666,4733934.55166336, + 65700,5413446.99440722,1686864.70430337,3668199.77871656, + 6.6e4,5392862.16834428,3434394.71396246,2178475.13297448, + 66300,4750196.32199498,4785751.66545322,436744.445152622, + 66600,3559796.52739365,5585316.7296347,-1355528.90652471, + 66900,1959597.20578925,5742025.63690215,-2991415.6046114, + 67200,134376.536240933,5239666.35451623,-4283065.85614241, + 67500,-1706331.07059426,4137818.56427346,-5083382.64756209, + 67800,-3352491.96081032,2563922.03332514,-5302226.99750498, + 68100,-4617064.16569915,698116.184533239,-4915763.52743991, + 68400,-5356466.08495862,-1247101.52462867,-3968546.00809847, + 68700,-5486336.41852137,-3050374.47831377,-2568465.25001707, + 6.9e4,-4991155.60227331,-4506072.59048139,-874966.280892015, + 69106.00003200001,-4675088.88357972,-4904462.29220647,-242107.371110211, + 69106.00003200001,-4674963.43006193,-4904676.83927901,-241826.66183195, + 69300,-3926327.89260015,-5447739.6656541,918866.020571072, + 69600,-2413003.0971971,-5766684.33045901,2607080.91832284, + 69900,-623679.307706445,-5425959.09823221,3996179.06378573, + 70200,1237024.11335224,-4463927.32507537,4926346.27837966, + 70500,2955874.28281264,-2990202.28421801,5290146.7654427, + 70800,4335347.20816686,-1173262.20585629,5044913.31517192, + 71100,5216242.70405186,778530.799227653,4217907.07731278, + 71400,5496260.69417988,2640589.40930168,2903682.37616923, + 71700,5142403.54666242,4198089.22501779,1253644.11189087, + 7.2e4,4195371.21443519,5271357.13164593,-541431.220697357, + 72300,2764921.48219317,5737302.94797553,-2274011.7559695, + 72600,1016577.67885122,5543794.51304654,-3744557.13007387, + 72900,-848361.242067968,4715035.78742378,-4784900.89171273, + 73200,-2616507.19175234,3347680.53244974,-5277212.503935, + 73500,-4086622.1877807,1598870.37343154,-5166586.67068703, + 73800,-5091813.77614537,-331870.766672772,-4466459.62473102, + 74100,-5517764.43038847,-2224840.34855441,-3256788.83205051, + 74400,-5315467.41998746,-3864448.48360322,-1675276.49132697, + 74700,-4507114.79181934,-5063389.5046787,97748.034459793, + 7.5e4,-3184124.73189051,-5684120.41792651,1859531.62860591, + 75300,-1497109.88999113,-5654952.06546536,3408062.23671877, + 75600,361222.120358981,-4978554.80719553,4565381.47677838, + 75900,2178126.06100545,-3731775.80119291,5198117.73976067, + 76200,3745113.46822549,-2056944.55535702,5232830.32818793, + 76500,4881743.39892134,-145833.276595929,4664585.42136462, + 76800,5456445.04082095,1782000.8778115,3557929.78632622, + 77100,5402144.12356169,3504373.03321367,2039988.19446098, + 77400,4724667.04921966,4822486.7484427,286049.40318698, + 77700,3502425.90876414,5584603.2025813,-1501008.43043863, + 7.8e4,1877042.16532639,5704035.19530578,-3114913.22994025, + 78300,36152.4433097746,5168885.3009109,-4370435.33319926, + 78600,-1808985.8806344,4042459.56852687,-5124695.32426894, + 78900,-3447895.4230698,2454929.90464636,-5292838.48153945, + 79200,-4694376.04083089,587917.246969181,-4856779.60954812, + 79500,-5406874.18834785,-1345971.57419841,-3866678.85024478, + 79800,-5504046.34498431,-3126649.11627623,-2435299.58352432, + 80100,-4974076.82345692,-4551020.79550854,-725685.023696793, + 80400,-3876520.14799315,-5456025.84259603,1066926.93178847, + 80700,-2336019.11244069,-5737550.10811308,2737230.19518184, + 8.1e4,-528308.767769235,-5362697.09411111,4093467.41014218, + 81300,1339863.00009893,-4373750.04288914,4979577.30146014, + 81600,3054380.83966471,-2883413.7415267,5293162.12728486, + 81900,4418177.53111484,-1062089.46683839,4997306.49647254, + 82200,5273810.55541913,881318.380504194,4125099.72024297, + 82500,5521868.59835782,2723142.07392809,2776343.07581227, + 82800,5133062.42720615,4250871.17037253,1106480.61319088, + 83100,4152176.15897934,5288297.93859615,-691394.68983223, + 83400,2692908.6058646,5716530.34787588,-2409460.98637571, + 83700,924084.940881169,5487829.34165832,-3849936.15782554, + 8.4e4,-950712.293476668,4630433.68817126,-4848200.08467262, + 84300,-2717045.86353999,3244208.36976243,-5291286.81070567, + 84600,-4173926.65526437,1488361.55584656,-5129895.26438968, + 84900,-5155955.37745777,-436837.457277556,-4383197.33262401, + 85200,-5551402.34469437,-2312317.78907392,-3136419.38414599, + 85500,-5314693.03520338,-3924443.17760687,-1531500.88583952, + 85800,-4471936.40864901,-5089002.50775752,248512.950876881, + 86100,-3118503.91090586,-5672365.37553485,2000019.0666746, + 86400,-1408522.57228522,-5607125.68800775,3522147.91298588 + ] + } + }, + { + "id":"CesiumDrone", + "availability":"2012-03-15T10:00:00Z/2012-03-16T10:00:00Z", + "path":{ + "material":{ + "solidColor":{ + "color":{ + "interval":"2012-03-15T10:00:00Z/2012-03-16T10:00:00Z", + "rgba":[ + 255,255,0,255 + ] + } + } + }, + "width":[ + { + "interval":"2012-03-15T10:00:00Z/2012-03-16T10:00:00Z", + "number":2 + } + ], + "show":[ + { + "interval":"2012-03-15T10:00:00Z/2012-03-16T10:00:00Z", + "boolean":true + } + ] + }, + "model":{ + "gltf":"models/CesiumDrone/CesiumDrone.glb", + "minimumPixelSize":64, + "maximumScale":20000 + }, + "position":{ + "interpolationAlgorithm":"LINEAR", + "epoch":"2012-03-15T10:00:00Z", + "cartesian":[ + 0, -2001494.7936076433, -4745182.384226333, 3752246.7106542974, + 0.01, -2001494.6749603446, -4745182.387314641, 3752246.472892279, + 0.02, -2001494.5645944183, -4745182.367867421, 3752246.251494708, + 0.03, -2001494.462462647, -4745182.326031295, 3752246.046368467, + 0.04, -2001494.3685178126, -4745182.261952886, 3752245.8574204384, + 0.05, -2001494.2827126977, -4745182.1757788155, 3752245.6845575054, + 0.06, -2001494.2050000841, -4745182.067655706, 3752245.527686548, + 0.07, -2001494.1353327534, -4745181.93773018, 3752245.38671445, + 0.08, -2001494.0736634883, -4745181.786148861, 3752245.2615480954, + 0.09, -2001494.0199450708, -4745181.6130583715, 3752245.1520943646, + 0.1, -2001493.974130283, -4745181.418605333, 3752245.05826014, + 0.11, -2001493.9361719068, -4745181.202936369, 3752244.979952306, + 0.12, -2001493.9060227247, -4745180.966198101, 3752244.9170777425, + 0.13, -2001493.8836355186, -4745180.708537152, 3752244.8695433345, + 0.14, -2001493.8689630704, -4745180.430100145, 3752244.8372559627, + 0.15, -2001493.8619581622, -4745180.131033702, 3752244.8201225093, + 0.16, -2001493.8625735764, -4745179.811484443, 3752244.818049858, + 0.17, -2001493.870762095, -4745179.471598997, 3752244.83094489, + 0.18, -2001493.8864765004, -4745179.111523979, 3752244.858714489, + 0.19, -2001493.9096695741, -4745178.731406017, 3752244.901265537, + 0.2, -2001493.9402940986, -4745178.331391732, 3752244.9585049157, + 0.21, -2001493.9783028557, -4745177.911627744, 3752245.0303395083, + 0.22, -2001494.0236486276, -4745177.472260679, 3752245.1166761965, + 0.23, -2001494.0762841967, -4745177.0134371575, 3752245.2174218637, + 0.24, -2001494.1361623448, -4745176.535303803, 3752245.332483392, + 0.25, -2001494.203235854, -4745176.038007238, 3752245.461767663, + 0.26, -2001494.277457507, -4745175.521694084, 3752245.605181561, + 0.27, -2001494.3587800849, -4745174.986510963, 3752245.7626319663, + 0.28, -2001494.4471563704, -4745174.4326045, 3752245.9340257626, + 0.29, -2001494.5425391458, -4745173.860121314, 3752246.119269832, + 0.3, -2001494.6448811926, -4745173.269208032, 3752246.318271057, + 0.31, -2001494.7541352934, -4745172.660011273, 3752246.5309363203, + 0.32, -2001494.8702542305, -4745172.032677661, 3752246.757172504, + 0.33, -2001494.993190785, -4745171.387353817, 3752246.9968864904, + 0.34, -2001495.1228977398, -4745170.724186365, 3752247.2499851626, + 0.35000000000000003, -2001495.2593278768, -4745170.043321926, 3752247.5163754015, + 0.36, -2001495.4024339784, -4745169.344907125, 3752247.795964092, + 0.37, -2001495.552168826, -4745168.629088583, 3752248.088658114, + 0.38, -2001495.7084852026, -4745167.896012922, 3752248.394364352, + 0.39, -2001495.8713358897, -4745167.145826766, 3752248.7129896865, + 0.4, -2001496.0406736694, -4745166.378676737, 3752249.0444410015, + 0.41000000000000003, -2001496.2164513243, -4745165.594709455, 3752249.388625179, + 0.42, -2001496.3986216357, -4745164.794071546, 3752249.745449101, + 0.43, -2001496.5871373867, -4745163.976909631, 3752250.114819651, + 0.44, -2001496.7819513585, -4745163.143370332, 3752250.4966437095, + 0.45, -2001496.9830163338, -4745162.293600273, 3752250.890828161, + 0.46, -2001497.1902850943, -4745161.427746075, 3752251.2972798864, + 0.47000000000000003, -2001497.4037104223, -4745160.545954361, 3752251.7159057693, + 0.48, -2001497.6232451003, -4745159.648371754, 3752252.1466126917, + 0.49, -2001497.8488419093, -4745158.735144875, 3752252.5893075354, + 0.5, -2001498.0804536324, -4745157.8064203495, 3752253.043897184, + 0.51, -2001498.3180330514, -4745156.862344796, 3752253.5102885184, + 0.52, -2001498.5615329486, -4745155.9030648405, 3752253.9883884233, + 0.53, -2001498.8109061054, -4745154.928727103, 3752254.4781037783, + 0.54, -2001499.0661053048, -4745153.939478209, 3752254.979341468, + 0.55, -2001499.3270833283, -4745152.935464778, 3752255.4920083745, + 0.56, -2001499.5937929589, -4745151.916833434, 3752256.01601138, + 0.5700000000000001, -2001499.866186977, -4745150.883730797, 3752256.551257367, + 0.58, -2001500.1442181661, -4745149.836303494, 3752257.097653216, + 0.59, -2001500.4278393078, -4745148.774698145, 3752257.655105813, + 0.6, -2001500.7170031848, -4745147.699061372, 3752258.2235220387, + 0.61, -2001501.0116625784, -4745146.609539798, 3752258.8028087746, + 0.62, -2001501.311770271, -4745145.506280047, 3752259.392872905, + 0.63, -2001501.6172790448, -4745144.3894287385, 3752259.99362131, + 0.64, -2001501.9281416822, -4745143.259132497, 3752260.6049608747, + 0.65, -2001502.2443109641, -4745142.115537945, 3752261.226798479, + 0.66, -2001502.565739674, -4745140.958791705, 3752261.8590410072, + 0.67, -2001502.8923805936, -4745139.789040399, 3752262.5015953407, + 0.68, -2001503.2241865045, -4745138.606430649, 3752263.1543683624, + 0.6900000000000001, -2001503.5611101894, -4745137.411109079, 3752263.817266955, + 0.7000000000000001, -2001503.90310443, -4745136.20322231, 3752264.4901980013, + 0.71, -2001504.2501220086, -4745134.982916965, 3752265.1730683814, + 0.72, -2001504.6021157072, -4745133.750339667, 3752265.8657849813, + 0.73, -2001504.9590383081, -4745132.5056370385, 3752266.5682546794, + 0.74, -2001505.3208425934, -4745131.2489557015, 3752267.2803843617, + 0.75, -2001505.6874813447, -4745129.980442279, 3752268.002080909, + 0.76, -2001506.0589073447, -4745128.700243392, 3752268.7332512033, + 0.77, -2001506.4350733752, -4745127.408505664, 3752269.4738021283, + 0.78, -2001506.815932219, -4745126.105375719, 3752270.2236405653, + 0.79, -2001507.2014366568, -4745124.791000178, 3752270.9826733973, + 0.8, -2001507.5915394716, -4745123.465525664, 3752271.7508075065, + 0.81, -2001507.9861934455, -4745122.129098799, 3752272.527949776, + 0.8200000000000001, -2001508.3853513605, -4745120.781866206, 3752273.3140070876, + 0.8300000000000001, -2001508.7889659987, -4745119.423974507, 3752274.108886324, + 0.84, -2001509.1969901423, -4745118.055570323, 3752274.912494367, + 0.85, -2001509.6093765735, -4745116.67680028, 3752275.7247380996, + 0.86, -2001510.026078074, -4745115.287810999, 3752276.545524404, + 0.87, -2001510.4470474261, -4745113.888749102, 3752277.3747601635, + 0.88, -2001510.872237412, -4745112.479761211, 3752278.2123522596, + 0.89, -2001511.3016008136, -4745111.060993951, 3752279.0582075743, + 0.9, -2001511.7350904134, -4745109.632593941, 3752279.912232992, + 0.91, -2001512.172658993, -4745108.194707805, 3752280.7743353923, + 0.92, -2001512.6142593345, -4745106.747482166, 3752281.64442166, + 0.93, -2001513.0598442205, -4745105.291063646, 3752282.5223986763, + 0.9400000000000001, -2001513.509366433, -4745103.825598869, 3752283.408173325, + 0.9500000000000001, -2001513.9627787536, -4745102.351234455, 3752284.301652487, + 0.96, -2001514.420033965, -4745100.868117028, 3752285.202743045, + 0.97, -2001514.881084849, -4745099.376393211, 3752286.1113518816, + 0.98, -2001515.3458841878, -4745097.876209625, 3752287.02738588, + 0.99, -2001515.8143847631, -4745096.367712893, 3752287.950751922, + 1, -2001516.286539358, -4745094.851049638, 3752288.881356889, + 1.01, -2001516.7623007535, -4745093.326366483, 3752289.8191076657, + 1.02, -2001517.2416217327, -4745091.793810048, 3752290.7639111327, + 1.03, -2001517.7244550765, -4745090.253526959, 3752291.715674173, + 1.04, -2001518.210753568, -4745088.7056638375, 3752292.6743036695, + 1.05, -2001518.7004699889, -4745087.150367304, 3752293.6397065036, + 1.06, -2001519.1935571216, -4745085.587783982, 3752294.6117895595, + 1.07, -2001519.6899677478, -4745084.018060493, 3752295.590459718, + 1.08, -2001520.1896546497, -4745082.441343465, 3752296.5756238624, + 1.09, -2001520.69257061, -4745080.857779513, 3752297.567188874, + 1.1, -2001521.1986684096, -4745079.267515263, 3752298.565061636, + 1.11, -2001521.7079008317, -4745077.670697339, 3752299.5691490313, + 1.12, -2001522.220220658, -4745076.06747236, 3752300.5793579416, + 1.1300000000000001, -2001522.7355806706, -4745074.457986952, 3752301.5955952494, + 1.1400000000000001, -2001523.2539336514, -4745072.842387734, 3752302.617767838, + 1.1500000000000001, -2001523.775232383, -4745071.22082133, 3752303.6457825885, + 1.16, -2001524.299429647, -4745069.593434365, 3752304.6795463846, + 1.17, -2001524.8264782259, -4745067.9603734575, 3752305.718966108, + 1.18, -2001525.3563309014, -4745066.321785234, 3752306.7639486413, + 1.19, -2001525.8889404559, -4745064.677816312, 3752307.8144008666, + 1.2, -2001526.424259672, -4745063.028613319, 3752308.870229667, + 1.21, -2001526.9622413304, -4745061.374322874, 3752309.9313419247, + 1.22, -2001527.5028382144, -4745059.715091602, 3752310.9976445218, + 1.23, -2001528.0460031058, -4745058.051066124, 3752312.0690443404, + 1.24, -2001528.5916887866, -4745056.382393062, 3752313.1454482647, + 1.25, -2001529.1398480386, -4745054.709219039, 3752314.226763175, + 1.26, -2001529.690433645, -4745053.031690679, 3752315.312895955, + 1.27, -2001530.2433983868, -4745051.349954603, 3752316.403753487, + 1.28, -2001530.7986950462, -4745049.664157433, 3752317.499242652, + 1.29, -2001531.3562764057, -4745047.974445794, 3752318.5992703354, + 1.3, -2001531.9160952475, -4745046.280966306, 3752319.703743417, + 1.31, -2001532.478104353, -4745044.583865592, 3752320.8125687805, + 1.32, -2001533.042256505, -4745042.883290276, 3752321.9256533077, + 1.33, -2001533.6085044858, -4745041.179386979, 3752323.0429038815, + 1.34, -2001534.1768010769, -4745039.472302323, 3752324.1642273846, + 1.35, -2001534.7470990603, -4745037.762182932, 3752325.289530698, + 1.36, -2001535.3193512184, -4745036.049175427, 3752326.418720706, + 1.37, -2001535.8935103333, -4745034.333426434, 3752327.55170429, + 1.3800000000000001, -2001536.469529187, -4745032.615082571, 3752328.6883883313, + 1.3900000000000001, -2001537.0473605623, -4745030.894290462, 3752329.8286797157, + 1.4000000000000001, -2001537.6269572403, -4745029.171196731, 3752330.972485322, + 1.41, -2001538.2082720036, -4745027.445947998, 3752332.1197120347, + 1.42, -2001538.7912576338, -4745025.718690887, 3752333.2702667355, + 1.43, -2001539.375866914, -4745023.989572022, 3752334.4240563074, + 1.44, -2001539.9620526256, -4745022.258738022, 3752335.580987633, + 1.45, -2001540.5497675505, -4745020.526335513, 3752336.740967593, + 1.46, -2001541.1389644712, -4745018.792511116, 3752337.903903072, + 1.47, -2001541.7295961701, -4745017.057411453, 3752339.0697009508, + 1.48, -2001542.3216154287, -4745015.321183145, 3752340.238268113, + 1.49, -2001542.9149750294, -4745013.583972818, 3752341.4095114414, + 1.5, -2001543.5096277539, -4745011.845927093, 3752342.5833378164, + 1.51, -2001544.1055263847, -4745010.107192593, 3752343.7596541224, + 1.52, -2001544.7026237042, -4745008.36791594, 3752344.93836724, + 1.53, -2001545.3008724942, -4745006.628243755, 3752346.119384054, + 1.54, -2001545.9002255364, -4745004.888322663, 3752347.302611445, + 1.55, -2001546.5006356132, -4745003.148299283, 3752348.487956296, + 1.56, -2001547.102055507, -4745001.4083202435, 3752349.675325489, + 1.57, -2001547.7044379995, -4744999.668532162, 3752350.8646259075, + 1.58, -2001548.307735873, -4744997.929081662, 3752352.055764433, + 1.59, -2001548.9119019099, -4744996.190115367, 3752353.2486479487, + 1.6, -2001549.5168888916, -4744994.451779898, 3752354.443183336, + 1.61, -2001550.1226496007, -4744992.71422188, 3752355.6392774777, + 1.62, -2001550.7291368193, -4744990.977587933, 3752356.8368372573, + 1.6300000000000001, -2001551.336303329, -4744989.242024681, 3752358.0357695557, + 1.6400000000000001, -2001551.944101913, -4744987.507678745, 3752359.2359812562, + 1.6500000000000001, -2001552.552485352, -4744985.77469675, 3752360.437379241, + 1.6600000000000001, -2001553.1614064288, -4744984.043225315, 3752361.6398703926, + 1.67, -2001553.7708179257, -4744982.313411066, 3752362.843361594, + 1.68, -2001554.3806726248, -4744980.585400623, 3752364.047759726, + 1.69, -2001554.990923308, -4744978.859340611, 3752365.2529716725, + 1.7, -2001555.6015227572, -4744977.13537765, 3752366.4589043153, + 1.71, -2001556.2124237546, -4744975.413658364, 3752367.6654645377, + 1.72, -2001556.8235790825, -4744973.694329374, 3752368.872559221, + 1.73, -2001557.434941523, -4744971.977537303, 3752370.080095248, + 1.74, -2001558.0464638583, -4744970.263428776, 3752371.2879795013, + 1.75, -2001558.65809887, -4744968.5521504115, 3752372.4961188645, + 1.76, -2001559.2697993407, -4744966.843848835, 3752373.704420217, + 1.77, -2001559.8815180522, -4744965.138670668, 3752374.9127904447, + 1.78, -2001560.4932077872, -4744963.436762533, 3752376.1211364274, + 1.79, -2001561.1048213267, -4744961.738271052, 3752377.3293650486, + 1.8, -2001561.7163114536, -4744960.043342849, 3752378.5373831904, + 1.81, -2001562.32763095, -4744958.352124545, 3752379.745097736, + 1.82, -2001562.9387325977, -4744956.664762763, 3752380.9524155674, + 1.83, -2001563.549569179, -4744954.981404126, 3752382.1592435664, + 1.84, -2001564.160093476, -4744953.302195256, 3752383.3654886163, + 1.85, -2001564.7702582711, -4744951.627282775, 3752384.5710575995, + 1.86, -2001565.3800163455, -4744949.956813306, 3752385.7758573983, + 1.87, -2001565.9893204821, -4744948.29093347, 3752386.9797948953, + 1.8800000000000001, -2001566.5981234626, -4744946.6297898935, 3752388.1827769713, + 1.8900000000000001, -2001567.2063780697, -4744944.973529196, 3752389.3847105107, + 1.9000000000000001, -2001567.8140370846, -4744943.322298, 3752390.5855023963, + 1.9100000000000001, -2001568.4210532904, -4744941.676242929, 3752391.785059509, + 1.92, -2001569.0273794683, -4744940.035510604, 3752392.983288732, + 1.93, -2001569.6329684006, -4744938.40024765, 3752394.1800969467, + 1.94, -2001570.2377728699, -4744936.770600688, 3752395.3753910367, + 1.95, -2001570.841745658, -4744935.14671634, 3752396.5690778843, + 1.96, -2001571.4448395472, -4744933.528741228, 3752397.7610643706, + 1.97, -2001572.047007319, -4744931.916821978, 3752398.9512573807, + 1.98, -2001572.648201756, -4744930.3111052085, 3752400.139563795, + 1.99, -2001573.2483756403, -4744928.711737543, 3752401.3258904964, + 2, -2001573.847481754, -4744927.118865605, 3752402.510144368, + 2.0100000000000002, -2001574.4454728789, -4744925.5326360185, 3752403.692232291, + 2.02, -2001575.0423017975, -4744923.9531954015, 3752404.8720611483, + 2.0300000000000002, -2001575.6379212916, -4744922.380690381, 3752406.0495378226, + 2.04, -2001576.2322841436, -4744920.815267576, 3752407.224569196, + 2.05, -2001576.825343135, -4744919.257073613, 3752408.3970621517, + 2.06, -2001577.417051049, -4744917.706255108, 3752409.566923571, + 2.07, -2001578.0073606663, -4744916.162958691, 3752410.7340603373, + 2.08, -2001578.5962247702, -4744914.62733098, 3752411.898379333, + 2.09, -2001579.1835961423, -4744913.099518599, 3752413.059787439, + 2.1, -2001579.7694275647, -4744911.579668171, 3752414.21819154, + 2.11, -2001580.3536718194, -4744910.067926316, 3752415.373498517, + 2.12, -2001580.9362816887, -4744908.564439657, 3752416.525615253, + 2.13, -2001581.5172099548, -4744907.06935482, 3752417.6744486303, + 2.14, -2001582.0964093998, -4744905.582818424, 3752418.8199055307, + 2.15, -2001582.6738328054, -4744904.104977094, 3752419.9618928377, + 2.16, -2001583.249432954, -4744902.635977449, 3752421.1003174335, + 2.17, -2001583.8231626276, -4744901.175966115, 3752422.2350862, + 2.18, -2001584.3949746087, -4744899.725089713, 3752423.36610602, + 2.19, -2001584.9648216788, -4744898.2834948655, 3752424.493283775, + 2.2, -2001585.5326566203, -4744896.851328195, 3752425.616526349, + 2.21, -2001586.0984322154, -4744895.428736324, 3752426.735740623, + 2.22, -2001586.6621012457, -4744894.015865876, 3752427.850833481, + 2.23, -2001587.223616494, -4744892.612863472, 3752428.961711805, + 2.24, -2001587.7829307423, -4744891.219875734, 3752430.0682824757, + 2.25, -2001588.3399967724, -4744889.837049288, 3752431.1704523778, + 2.2600000000000002, -2001588.8947673663, -4744888.464530752, 3752432.2681283923, + 2.27, -2001589.4471953067, -4744887.102466752, 3752433.361217403, + 2.2800000000000002, -2001589.9972333747, -4744885.75100391, 3752434.4496262902, + 2.29, -2001590.5448343533, -4744884.410288846, 3752435.5332619385, + 2.3000000000000003, -2001591.0899510244, -4744883.080468183, 3752436.6120312293, + 2.31, -2001591.6327160893, -4744881.76141283, 3752437.686150879, + 2.32, -2001592.1776619446, -4744880.446213825, 3752438.7634201646, + 2.33, -2001592.7267464276, -4744879.131811127, 3752439.8472201196, + 2.34, -2001593.279928921, -4744877.818192568, 3752440.937492629, + 2.35, -2001593.83716881, -4744876.505345976, 3752442.0341795776, + 2.36, -2001594.3984254773, -4744875.193259181, 3752443.13722285, + 2.37, -2001594.963658308, -4744873.881920016, 3752444.246564331, + 2.38, -2001595.5328266846, -4744872.571316311, 3752445.3621459045, + 2.39, -2001596.1058899916, -4744871.261435894, 3752446.4839094565, + 2.4, -2001596.6828076139, -4744869.952266599, 3752447.6117968694, + 2.41, -2001597.2635389336, -4744868.643796251, 3752448.745750031, + 2.42, -2001597.8480433356, -4744867.336012685, 3752449.8857108233, + 2.43, -2001598.4362802038, -4744866.028903729, 3752451.0316211325, + 2.44, -2001599.0282089214, -4744864.722457214, 3752452.183422842, + 2.45, -2001599.623788873, -4744863.416660971, 3752453.341057838, + 2.46, -2001600.222979442, -4744862.11150283, 3752454.5044680038, + 2.47, -2001600.8257400123, -4744860.80697062, 3752455.6735952245, + 2.48, -2001601.4320299681, -4744859.503052173, 3752456.848381385, + 2.49, -2001602.0418086934, -4744858.199735317, 3752458.028768371, + 2.5, -2001602.6550355714, -4744856.897007885, 3752459.214698064, + 2.5100000000000002, -2001603.271669987, -4744855.594857707, 3752460.4061123524, + 2.52, -2001603.8916713228, -4744854.293272612, 3752461.602953118, + 2.5300000000000002, -2001604.5149989636, -4744852.992240431, 3752462.8051622473, + 2.54, -2001605.1416122927, -4744851.691748995, 3752464.0126816235, + 2.5500000000000003, -2001605.7714706946, -4744850.391786134, 3752465.2254531323, + 2.56, -2001606.4045335527, -4744849.092339676, 3752466.4434186583, + 2.57, -2001607.0407602508, -4744847.793397455, 3752467.666520086, + 2.58, -2001607.6801101733, -4744846.494947298, 3752468.8946993, + 2.59, -2001608.3225427035, -4744845.19697704, 3752470.1278981846, + 2.6, -2001608.9680172259, -4744843.899474505, 3752471.3660586253, + 2.61, -2001609.6164931231, -4744842.602427528, 3752472.609122506, + 2.62, -2001610.2679297808, -4744841.305823939, 3752473.8570317114, + 2.63, -2001610.9222865815, -4744840.009651566, 3752475.1097281273, + 2.64, -2001611.5795229096, -4744838.7138982415, 3752476.367153636, + 2.65, -2001612.239598149, -4744837.418551795, 3752477.629250125, + 2.66, -2001612.9024716835, -4744836.123600055, 3752478.895959477, + 2.67, -2001613.5681028967, -4744834.8290308565, 3752480.1672235783, + 2.68, -2001614.2364511734, -4744833.534832025, 3752481.442984311, + 2.69, -2001614.907475896, -4744832.240991393, 3752482.7231835625, + 2.7, -2001615.5811364495, -4744830.94749679, 3752484.0077632153, + 2.71, -2001616.2573922176, -4744829.65433605, 3752485.296665156, + 2.72, -2001616.936202584, -4744828.361496998, 3752486.5898312675, + 2.73, -2001617.6175269322, -4744827.068967466, 3752487.887203435, + 2.74, -2001618.301324647, -4744825.776735286, 3752489.188723544, + 2.75, -2001618.9875551118, -4744824.484788287, 3752490.4943334786, + 2.7600000000000002, -2001619.6761777098, -4744823.193114301, 3752491.8039751234, + 2.77, -2001620.367151826, -4744821.901701155, 3752493.117590363, + 2.7800000000000002, -2001621.0604368434, -4744820.610536682, 3752494.4351210827, + 2.79, -2001621.7559921464, -4744819.319608712, 3752495.756509166, + 2.8000000000000003, -2001622.4537771186, -4744818.028905075, 3752497.081696498, + 2.81, -2001623.1537511442, -4744816.7384136, 3752498.4106249646, + 2.82, -2001623.855873607, -4744815.44812212, 3752499.7432364486, + 2.83, -2001624.5601038905, -4744814.158018464, 3752501.079472836, + 2.84, -2001625.266401379, -4744812.868090463, 3752502.41927601, + 2.85, -2001625.974725456, -4744811.578325945, 3752503.762587857, + 2.86, -2001626.6850355056, -4744810.288712743, 3752505.109350261, + 2.87, -2001627.3972909115, -4744808.999238685, 3752506.459505106, + 2.88, -2001628.111451058, -4744807.709891603, 3752507.812994277, + 2.89, -2001628.8274753282, -4744806.420659328, 3752509.169759659, + 2.9, -2001629.5453231072, -4744805.131529689, 3752510.529743138, + 2.91, -2001630.2649537777, -4744803.842490517, 3752511.8928865953, + 2.92, -2001630.9863267243, -4744802.553529641, 3752513.2591319177, + 2.93, -2001631.7094013302, -4744801.264634892, 3752514.6284209904, + 2.94, -2001632.43413698, -4744799.975794102, 3752516.0006956975, + 2.95, -2001633.160493057, -4744798.686995099, 3752517.3758979226, + 2.96, -2001633.8884289453, -4744797.398225715, 3752518.7539695515, + 2.97, -2001634.6179040289, -4744796.10947378, 3752520.134852469, + 2.98, -2001635.3488776917, -4744794.820727122, 3752521.5184885585, + 2.99, -2001636.081309317, -4744793.531973574, 3752522.9048197055, + 3, -2001636.8151582892, -4744792.243200966, 3752524.293787795, + 3.0100000000000002, -2001637.5503839923, -4744790.954397128, 3752525.6853347123, + 3.02, -2001638.2869458098, -4744789.665549891, 3752527.07940234, + 3.0300000000000002, -2001639.024803126, -4744788.376647083, 3752528.4759325637, + 3.04, -2001639.7639153244, -4744787.087676537, 3752529.874867269, + 3.0500000000000003, -2001640.5042417888, -4744785.798626083, 3752531.2761483383, + 3.06, -2001641.245741903, -4744784.509483549, 3752532.6797176586, + 3.0700000000000003, -2001641.9883750516, -4744783.220236768, 3752534.0855171145, + 3.08, -2001642.732100618, -4744781.930873568, 3752535.493488589, + 3.09, -2001643.4768779862, -4744780.641381782, 3752536.9035739675, + 3.1, -2001644.22266654, -4744779.351749239, 3752538.315715136, + 3.11, -2001644.9694256624, -4744778.061963769, 3752539.729853976, + 3.12, -2001645.7171147391, -4744776.772013201, 3752541.1459323764, + 3.13, -2001646.465693152, -4744775.481885369, 3752542.5638922174, + 3.14, -2001647.2151202867, -4744774.191568101, 3752543.9836753868, + 3.15, -2001647.965355526, -4744772.9010492265, 3752545.4052237687, + 3.16, -2001648.7163582544, -4744771.610316578, 3752546.8284792467, + 3.17, -2001649.4680878553, -4744770.319357984, 3752548.2533837063, + 3.18, -2001650.2205037123, -4744769.028161276, 3752549.6798790325, + 3.19, -2001650.9735652106, -4744767.736714284, 3752551.1079071094, + 3.2, -2001651.7272317328, -4744766.4450048385, 3752552.5374098215, + 3.21, -2001652.481462663, -4744765.15302077, 3752553.9683290534, + 3.22, -2001653.2362173854, -4744763.860749909, 3752555.4006066895, + 3.23, -2001653.9914552837, -4744762.568180083, 3752556.8341846163, + 3.24, -2001654.7471357416, -4744761.275299125, 3752558.269004716, + 3.25, -2001655.5032181435, -4744759.982094867, 3752559.705008876, + 3.2600000000000002, -2001656.2596618726, -4744758.688555134, 3752561.1421389775, + 3.27, -2001657.0164263134, -4744757.394667762, 3752562.5803369083, + 3.2800000000000002, -2001657.7734708497, -4744756.100420579, 3752564.019544552, + 3.29, -2001658.5307548651, -4744754.805801415, 3752565.459703793, + 3.3000000000000003, -2001659.2882377428, -4744753.5107981, 3752566.900756515, + 3.31, -2001660.0458788679, -4744752.215398465, 3752568.342644605, + 3.3200000000000003, -2001660.8036376238, -4744750.919590341, 3752569.7853099452, + 3.33, -2001661.5614733943, -4744749.623361556, 3752571.2286944217, + 3.34, -2001662.3193455637, -4744748.326699944, 3752572.6727399197, + 3.35, -2001663.0772135153, -4744747.029593333, 3752574.117388323, + 3.36, -2001663.8350366333, -4744745.732029552, 3752575.562581516, + 3.37, -2001664.5927743008, -4744744.433996432, 3752577.0082613835, + 3.38, -2001665.350385903, -4744743.135481808, 3752578.4543698104, + 3.39, -2001666.1078308225, -4744741.836473504, 3752579.900848681, + 3.4, -2001666.865068445, -4744740.536959354, 3752581.3476398815, + 3.41, -2001667.6220581515, -4744739.236927187, 3752582.794685294, + 3.42, -2001668.3787593287, -4744737.936364833, 3752584.241926805, + 3.43, -2001669.135131359, -4744736.635260124, 3752585.6893062997, + 3.44, -2001669.891133627, -4744735.333600888, 3752587.1367656607, + 3.45, -2001670.6467255158, -4744734.0313749565, 3752588.5842467733, + 3.46, -2001671.4018664092, -4744732.72857016, 3752590.0316915233, + 3.47, -2001672.1565156921, -4744731.42517433, 3752591.4790417934, + 3.48, -2001672.9106327477, -4744730.121175296, 3752592.926239471, + 3.49, -2001673.6641769598, -4744728.816560886, 3752594.3732264386, + 3.5, -2001674.4171077125, -4744727.511318934, 3752595.819944582, + 3.5100000000000002, -2001675.1693843897, -4744726.205437266, 3752597.266335785, + 3.52, -2001675.9209663752, -4744724.898903716, 3752598.7123419326, + 3.5300000000000002, -2001676.6718130526, -4744723.591706115, 3752600.1579049095, + 3.54, -2001677.4218838066, -4744722.28383229, 3752601.602966601, + 3.5500000000000003, -2001678.171138019, -4744720.975270073, 3752603.0474688895, + 3.56, -2001678.9195350765, -4744719.666007295, 3752604.4913536627, + 3.5700000000000003, -2001679.6670343617, -4744718.356031785, 3752605.9345628037, + 3.58, -2001680.4135952583, -4744717.045331374, 3752607.3770381976, + 3.59, -2001681.1591771494, -4744715.733893892, 3752608.818721728, + 3.6, -2001681.903739421, -4744714.42170717, 3752610.2595552807, + 3.61, -2001682.647241455, -4744713.108759037, 3752611.69948074, + 3.62, -2001683.3896426363, -4744711.7950373255, 3752613.138439991, + 3.63, -2001684.1309023486, -4744710.480529864, 3752614.5763749173, + 3.64, -2001684.8709799754, -4744709.165224483, 3752616.013227404, + 3.65, -2001685.609834901, -4744707.849109014, 3752617.4489393365, + 3.66, -2001686.3474265097, -4744706.532171288, 3752618.8834525994, + 3.67, -2001687.083714184, -4744705.21439913, 3752620.3167090756, + 3.68, -2001687.8186573084, -4744703.895780376, 3752621.7486506514, + 3.69, -2001688.552215268, -4744702.576302855, 3752623.1792192124, + 3.7, -2001689.2843474443, -4744701.255954396, 3752624.608356641, + 3.71, -2001690.0150132228, -4744699.934722832, 3752626.0360048222, + 3.72, -2001690.7441719878, -4744698.612595991, 3752627.462105642, + 3.73, -2001691.471783122, -4744697.289561704, 3752628.886600985, + 3.74, -2001692.19780601, -4744695.9656078005, 3752630.3094327343, + 3.75, -2001692.9222000348, -4744694.640722111, 3752631.730542776, + 3.7600000000000002, -2001693.6449245815, -4744693.314892468, 3752633.149872994, + 3.77, -2001694.3659390335, -4744691.9881067, 3752634.567365274, + 3.7800000000000002, -2001695.085202773, -4744690.660352635, 3752635.982961498, + 3.79, -2001695.8026751874, -4744689.33161811, 3752637.3966035554, + 3.8000000000000003, -2001696.518315657, -4744688.001890948, 3752638.8082333254, + 3.81, -2001697.2320835681, -4744686.671158982, 3752640.217792696, + 3.8200000000000003, -2001697.9439383033, -4744685.339410045, 3752641.625223551, + 3.83, -2001698.6538392473, -4744684.006631965, 3752643.0304677757, + 3.84, -2001699.361745783, -4744682.672812573, 3752644.433467254, + 3.85, -2001700.0676172958, -4744681.337939697, 3752645.8341638716, + 3.86, -2001700.7714131677, -4744680.002001171, 3752647.232499512, + 3.87, -2001701.4730927837, -4744678.664984823, 3752648.6284160595, + 3.88, -2001702.1726155272, -4744677.326878484, 3752650.0218554, + 3.89, -2001702.8699407831, -4744675.987669984, 3752651.4127594177, + 3.9, -2001703.565027934, -4744674.647347153, 3752652.8010699977, + 3.91, -2001704.2578363644, -4744673.305897824, 3752654.186729023, + 3.92, -2001704.9483254577, -4744671.963309822, 3752655.5696783806, + 3.93, -2001705.636454599, -4744670.619570982, 3752656.9498599544, + 3.94, -2001706.3221831704, -4744669.274669133, 3752658.3272156273, + 3.95, -2001707.0054705571, -4744667.928592104, 3752659.7016872857, + 3.96, -2001707.6862761425, -4744666.58132773, 3752661.0732168145, + 3.97, -2001708.3645593105, -4744665.232863835, 3752662.441746097, + 3.98, -2001709.0402794452, -4744663.883188252, 3752663.8072170196, + 3.99, -2001709.7133959297, -4744662.532288813, 3752665.1695714653, + 4, -2001710.3838681488, -4744661.180153347, 3752666.52875132, + 4.01, -2001711.051655486, -4744659.826769683, 3752667.8846984673, + 4.0200000000000005, -2001711.7167173256, -4744658.472125653, 3752669.2373547936, + 4.03, -2001712.3790130506, -4744657.116209087, 3752670.586662181, + 4.04, -2001713.0385020457, -4744655.759007814, 3752671.932562516, + 4.05, -2001713.6951436945, -4744654.400509668, 3752673.2749976823, + 4.0600000000000005, -2001714.3488973803, -4744653.040702475, 3752674.613909566, + 4.07, -2001714.9997224878, -4744651.679574068, 3752675.9492400507, + 4.08, -2001715.6475784003, -4744650.3171122745, 3752677.28093102, + 4.09, -2001716.2924245019, -4744648.95330493, 3752678.608924361, + 4.1, -2001716.9342201767, -4744647.58813986, 3752679.9331619563, + 4.11, -2001717.5729248088, -4744646.221604898, 3752681.253585693, + 4.12, -2001718.208497781, -4744644.85368787, 3752682.5701374523, + 4.13, -2001718.840898478, -4744643.484376612, 3752683.882759121, + 4.14, -2001719.4700862837, -4744642.113658951, 3752685.1913925842, + 4.15, -2001720.0960205814, -4744640.741522716, 3752686.495979725, + 4.16, -2001720.7186607558, -4744639.367955741, 3752687.796462429, + 4.17, -2001721.3379661902, -4744637.992945855, 3752689.092782582, + 4.18, -2001721.9538962683, -4744636.616480886, 3752690.3848820664, + 4.19, -2001722.5664103746, -4744635.238548667, 3752691.6727027674, + 4.2, -2001723.175467892, -4744633.859137026, 3752692.95618657, + 4.21, -2001723.7810282058, -4744632.478233797, 3752694.23527536, + 4.22, -2001724.3830506988, -4744631.095826808, 3752695.5099110208, + 4.23, -2001724.9814947553, -4744629.71190389, 3752696.7800354376, + 4.24, -2001725.5763197588, -4744628.326452871, 3752698.0455904943, + 4.25, -2001726.1674850932, -4744626.939461582, 3752699.306518076, + 4.26, -2001726.7549501434, -4744625.550917858, 3752700.5627600676, + 4.2700000000000005, -2001727.3386742922, -4744624.160809525, 3752701.814258354, + 4.28, -2001727.9186169228, -4744622.769124413, 3752703.0609548185, + 4.29, -2001728.494737421, -4744621.375850354, 3752704.302791348, + 4.3, -2001729.0669951695, -4744619.980975179, 3752705.539709825, + 4.3100000000000005, -2001729.6353495521, -4744618.584486716, 3752706.771652135, + 4.32, -2001730.199759953, -4744617.186372796, 3752707.9985601623, + 4.33, -2001730.7601857563, -4744615.786621251, 3752709.2203757935, + 4.34, -2001731.3165863454, -4744614.385219909, 3752710.43704091, + 4.3500000000000005, -2001731.8689211044, -4744612.982156602, 3752711.648497399, + 4.36, -2001732.4171494169, -4744611.577419161, 3752712.854687144, + 4.37, -2001732.961230667, -4744610.170995413, 3752714.05555203, + 4.38, -2001733.5011242393, -4744608.762873193, 3752715.251033943, + 4.39, -2001734.0367895162, -4744607.353040327, 3752716.441074765, + 4.4, -2001734.568185883, -4744605.941484649, 3752717.6256163823, + 4.41, -2001735.0952727222, -4744604.528193985, 3752718.804600679, + 4.42, -2001735.6180094192, -4744603.1131561715, 3752719.977969541, + 4.43, -2001736.1363553563, -4744601.696359032, 3752721.145664851, + 4.44, -2001736.6502699184, -4744600.2777904, 3752722.307628495, + 4.45, -2001737.1597124883, -4744598.857438105, 3752723.463802356, + 4.46, -2001737.6646424516, -4744597.435289981, 3752724.614128322, + 4.47, -2001738.165019191, -4744596.011333853, 3752725.758548274, + 4.48, -2001738.6608020905, -4744594.585557555, 3752726.8970040996, + 4.49, -2001739.1519505344, -4744593.157948917, 3752728.029437681, + 4.5, -2001739.6384239064, -4744591.728495766, 3752729.1557909055, + 4.51, -2001740.1201815906, -4744590.297185939, 3752730.276005657, + 4.5200000000000005, -2001740.5971829693, -4744588.864007259, 3752731.390023817, + 4.53, -2001741.069387429, -4744587.428947561, 3752732.497787275, + 4.54, -2001741.5367543516, -4744585.991994673, 3752733.599237912, + 4.55, -2001741.9992431207, -4744584.553136425, 3752734.6943176123, + 4.5600000000000005, -2001742.4568131221, -4744583.112360651, 3752735.782968265, + 4.57, -2001742.909423738, -4744581.669655177, 3752736.8651317502, + 4.58, -2001743.3570343528, -4744580.225007836, 3752737.940749955, + 4.59, -2001743.7996043516, -4744578.778406458, 3752739.009764764, + 4.6000000000000005, -2001744.2370931157, -4744577.329838871, 3752740.07211806, + 4.61, -2001744.669460031, -4744575.879292907, 3752741.12775173, + 4.62, -2001745.0970869055, -4744574.427482435, 3752742.1766900457, + 4.63, -2001745.5229124152, -4744572.979474777, 3752743.2194917803, + 4.64, -2001745.947499207, -4744571.5361846825, 3752744.256312205, + 4.65, -2001746.370836397, -4744570.09753558, 3752745.2871995918, + 4.66, -2001746.7929131014, -4744568.663450893, 3752746.312202213, + 4.67, -2001747.213718437, -4744567.233854045, 3752747.33136834, + 4.68, -2001747.6332415189, -4744565.808668458, 3752748.344746246, + 4.69, -2001748.0514714646, -4744564.387817559, 3752749.3523842026, + 4.7, -2001748.4683973899, -4744562.971224771, 3752750.354330481, + 4.71, -2001748.8840084113, -4744561.558813518, 3752751.3506333544, + 4.72, -2001749.298293645, -4744560.150507223, 3752752.341341094, + 4.73, -2001749.7112422076, -4744558.746229312, 3752753.3265019734, + 4.74, -2001750.1228432148, -4744557.3459032085, 3752754.306164263, + 4.75, -2001750.5330857832, -4744555.949452334, 3752755.280376236, + 4.76, -2001750.9419590293, -4744554.556800118, 3752756.2491861633, + 4.7700000000000005, -2001751.3494520697, -4744553.167869979, 3752757.212642318, + 4.78, -2001751.7555540195, -4744551.782585344, 3752758.1707929717, + 4.79, -2001752.1602539963, -4744550.400869635, 3752759.123686397, + 4.8, -2001752.5635411157, -4744549.022646281, 3752760.0713708648, + 4.8100000000000005, -2001752.965404494, -4744547.6478387, 3752761.013894649, + 4.82, -2001753.3658332478, -4744546.2763703205, 3752761.9513060204, + 4.83, -2001753.7648164935, -4744544.908164563, 3752762.883653251, + 4.84, -2001754.1623433472, -4744543.543144854, 3752763.8109846143, + 4.8500000000000005, -2001754.558402925, -4744542.181234618, 3752764.73334838, + 4.86, -2001754.9529843435, -4744540.822357276, 3752765.650792822, + 4.87, -2001755.3460767188, -4744539.466436255, 3752766.563366211, + 4.88, -2001755.7376691673, -4744538.113394978, 3752767.471116821, + 4.89, -2001756.1277508056, -4744536.7631568685, 3752768.3740929225, + 4.9, -2001756.5163107496, -4744535.4156453535, 3752769.272342788, + 4.91, -2001756.9033381157, -4744534.070783853, 3752770.1659146897, + 4.92, -2001757.2888220204, -4744532.728495793, 3752771.0548569, + 4.93, -2001757.67275158, -4744531.388704599, 3752771.939217691, + 4.94, -2001758.05511591, -4744530.051333691, 3752772.819045333, + 4.95, -2001758.4359041278, -4744528.7163064955, 3752773.6943881004, + 4.96, -2001758.8151053495, -4744527.383546439, 3752774.565294264, + 4.97, -2001759.192708691, -4744526.052976942, 3752775.4318120973, + 4.98, -2001759.5687032687, -4744524.724521429, 3752776.2939898698, + 4.99, -2001759.943078199, -4744523.398103327, 3752777.151875856, + 5, -2001760.315822598, -4744522.073646057, 3752778.0055183265, + 5.01, -2001760.6869255826, -4744520.751073043, 3752778.854965554, + 5.0200000000000005, -2001761.0563762684, -4744519.4303077115, 3752779.7002658104, + 5.03, -2001761.4241637725, -4744518.111273485, 3752780.541467368, + 5.04, -2001761.7902772103, -4744516.793893786, 3752781.3786184983, + 5.05, -2001762.1547056986, -4744515.478092042, 3752782.2117674747, + 5.0600000000000005, -2001762.5174383537, -4744514.163791675, 3752783.0409625676, + 5.07, -2001762.8784642918, -4744512.850916111, 3752783.8662520503, + 5.08, -2001763.2377726294, -4744511.539388769, 3752784.6876841947, + 5.09, -2001763.5953524825, -4744510.229133078, 3752785.5053072725, + 5.1000000000000005, -2001763.9511929674, -4744508.9200724615, 3752786.3191695563, + 5.11, -2001764.305283201, -4744507.612130342, 3752787.1293193162, + 5.12, -2001764.6576122986, -4744506.305230144, 3752787.935804827, + 5.13, -2001765.0081693777, -4744504.999295292, 3752788.73867436, + 5.14, -2001765.3569435535, -4744503.69424921, 3752789.5379761863, + 5.15, -2001765.703923943, -4744502.390015322, 3752790.3337585786, + 5.16, -2001766.0490996623, -4744501.086517052, 3752791.1260698093, + 5.17, -2001766.3924598277, -4744499.783677824, 3752791.9149581497, + 5.18, -2001766.7339935554, -4744498.481421062, 3752792.7004718725, + 5.19, -2001767.0736899623, -4744497.17967019, 3752793.4826592496, + 5.2, -2001767.4115381637, -4744495.878348634, 3752794.2615685533, + 5.21, -2001767.7475272764, -4744494.577379814, 3752795.0372480545, + 5.22, -2001768.0816464168, -4744493.276687157, 3752795.809746027, + 5.23, -2001768.4138847017, -4744491.976194088, 3752796.579110742, + 5.24, -2001768.7442312462, -4744490.675824029, 3752797.3453904726, + 5.25, -2001769.0726751674, -4744489.375500403, 3752798.1086334884, + 5.26, -2001769.3992055818, -4744488.075146639, 3752798.8688880643, + 5.2700000000000005, -2001769.723811605, -4744486.774686155, 3752799.6262024706, + 5.28, -2001770.046482354, -4744485.474042379, 3752800.38062498, + 5.29, -2001770.3672069444, -4744484.173138733, 3752801.132203864, + 5.3, -2001770.685974493, -4744482.871898643, 3752801.880987395, + 5.3100000000000005, -2001771.002774116, -4744481.570245531, 3752802.627023846, + 5.32, -2001771.3175949298, -4744480.268102823, 3752803.3703614883, + 5.33, -2001771.6304260504, -4744478.965393941, 3752804.111048593, + 5.34, -2001771.9412565944, -4744477.662042311, 3752804.8491334333, + 5.3500000000000005, -2001772.250075678, -4744476.357971357, 3752805.5846642824, + 5.36, -2001772.5568724177, -4744475.053104502, 3752806.3176894095, + 5.37, -2001772.8616359294, -4744473.747365169, 3752807.0482570897, + 5.38, -2001773.16435533, -4744472.440676786, 3752807.7764155935, + 5.39, -2001773.465019735, -4744471.132962774, 3752808.5022131917, + 5.4, -2001773.7636182613, -4744469.824146556, 3752809.225698159, + 5.41, -2001774.0601400253, -4744468.514151559, 3752809.946918766, + 5.42, -2001774.3545741427, -4744467.202901205, 3752810.6659232853, + 5.43, -2001774.6469097305, -4744465.89031892, 3752811.3827599883, + 5.44, -2001774.937135904, -4744464.576328126, 3752812.0974771474, + 5.45, -2001775.2252417807, -4744463.260852248, 3752812.8101230348, + 5.46, -2001775.5112164766, -4744461.943814711, 3752813.520745923, + 5.47, -2001775.7950491074, -4744460.625138938, 3752814.2293940824, + 5.48, -2001776.07672879, -4744459.304748353, 3752814.9361157883, + 5.49, -2001776.3562446402, -4744457.98256638, 3752815.6409593094, + 5.5, -2001776.633585775, -4744456.658516442, 3752816.34397292, + 5.51, -2001776.9087413105, -4744455.332521968, 3752817.045204891, + 5.5200000000000005, -2001777.1817003624, -4744454.004506376, 3752817.744703495, + 5.53, -2001777.4524520475, -4744452.674393092, 3752818.4425170035, + 5.54, -2001777.7209854822, -4744451.342105541, 3752819.138693689, + 5.55, -2001777.9872897821, -4744450.007567149, 3752819.8332818234, + 5.5600000000000005, -2001778.2513540646, -4744448.670701335, 3752820.5263296794, + 5.57, -2001778.5131674453, -4744447.331431527, 3752821.217885528, + 5.58, -2001778.7727190405, -4744445.989681147, 3752821.907997643, + 5.59, -2001779.029997967, -4744444.645373621, 3752822.5967142945, + 5.6000000000000005, -2001779.2849933403, -4744443.298432372, 3752823.284083755, + 5.61, -2001779.5376942775, -4744441.9487808235, 3752823.970154298, + 5.62, -2001779.7880898947, -4744440.596342401, 3752824.6549741942, + 5.63, -2001780.0361693078, -4744439.241040526, 3752825.338591716, + 5.64, -2001780.2819216338, -4744437.882798627, 3752826.0210551354, + 5.65, -2001780.5253359883, -4744436.521540123, 3752826.7024127254, + 5.66, -2001780.766401488, -4744435.157188442, 3752827.382712756, + 5.67, -2001781.0051072487, -4744433.789667005, 3752828.062003501, + 5.68, -2001781.241442388, -4744432.418899238, 3752828.7403332326, + 5.69, -2001781.47539602, -4744431.044808565, 3752829.417750222, + 5.7, -2001781.706957264, -4744429.66731841, 3752830.094302742, + 5.71, -2001781.9361152337, -4744428.286352197, 3752830.7700390634, + 5.72, -2001782.1628590466, -4744426.90183335, 3752831.44500746, + 5.73, -2001782.3871778185, -4744425.513685291, 3752832.1192562026, + 5.74, -2001782.609060666, -4744424.121831448, 3752832.792833564, + 5.75, -2001782.8284967048, -4744422.726195241, 3752833.4657878154, + 5.76, -2001783.0454750527, -4744421.326700099, 3752834.13816723, + 5.7700000000000005, -2001783.259984825, -4744419.923269441, 3752834.810020079, + 5.78, -2001783.4720151378, -4744418.515826694, 3752835.4813946346, + 5.79, -2001783.6815551072, -4744417.10429528, 3752836.1523391698, + 5.8, -2001783.888593851, -4744415.688598626, 3752836.8229019553, + 5.8100000000000005, -2001784.093120484, -4744414.268660154, 3752837.4931312646, + 5.82, -2001784.2951241226, -4744412.844403289, 3752838.163075368, + 5.83, -2001784.4945938839, -4744411.4157514535, 3752838.832782538, + 5.84, -2001784.691518884, -4744409.982628074, 3752839.5023010494, + 5.8500000000000005, -2001784.885888239, -4744408.544956572, 3752840.1716791713, + 5.86, -2001785.0776910651, -4744407.102660374, 3752840.8409651765, + 5.87, -2001785.2669164788, -4744405.655662902, 3752841.5102073373, + 5.88, -2001785.4535535963, -4744404.203887582, 3752842.179453925, + 5.89, -2001785.6375915336, -4744402.747257836, 3752842.848753213, + 5.9, -2001785.8190194075, -4744401.285697089, 3752843.518153473, + 5.91, -2001785.9978263343, -4744399.819128765, 3752844.1877029757, + 5.92, -2001786.1740014304, -4744398.347476289, 3752844.8574499954, + 5.93, -2001786.3475338123, -4744396.870663084, 3752845.5274428027, + 5.94, -2001786.5184125947, -4744395.388612574, 3752846.19772967, + 5.95, -2001786.686626896, -4744393.901248184, 3752846.86835887, + 5.96, -2001786.852165831, -4744392.408493337, 3752847.5393786724, + 5.97, -2001787.015018517, -4744390.910271457, 3752848.2108373526, + 5.98, -2001787.1751740696, -4744389.406505971, 3752848.882783181, + 5.99, -2001787.3326216058, -4744387.897120299, 3752849.5552644297, + 6, -2001787.4873502413, -4744386.382037868, 3752850.228329371, + 6.01, -2001787.6393490927, -4744384.861182096, 3752850.9020262756, + 6.0200000000000005, -2001787.788607276, -4744383.334476418, 3752851.576403419, + 6.03, -2001787.9351139076, -4744381.801844249, 3752852.25150907, + 6.04, -2001788.0788581045, -4744380.263209017, 3752852.9273915016, + 6.05, -2001788.219828982, -4744378.718494144, 3752853.6040989864, + 6.0600000000000005, -2001788.358015657, -4744377.167623056, 3752854.2816797956, + 6.07, -2001788.4934072462, -4744375.610519176, 3752854.9601822025, + 6.08, -2001788.6259928644, -4744374.047105929, 3752855.6396544785, + 6.09, -2001788.75576163, -4744372.477306738, 3752856.3201448955, + 6.1000000000000005, -2001788.882702657, -4744370.901045026, 3752857.0017017256, + 6.11, -2001789.0068050632, -4744369.31824422, 3752857.6843732414, + 6.12, -2001789.1280579655, -4744367.728827743, 3752858.3682077145, + 6.13, -2001789.2464504782, -4744366.1327190185, 3752859.053253417, + 6.140000000000001, -2001789.3619717192, -4744364.52984147, 3752859.7395586213, + 6.15, -2001789.4746108036, -4744362.920118521, 3752860.427171598, + 6.16, -2001789.5843568495, -4744361.303473598, 3752861.1161406217, + 6.17, -2001789.6911989716, -4744359.679830125, 3752861.8065139633, + 6.18, -2001789.795126287, -4744358.049111525, 3752862.4983398947, + 6.19, -2001789.8961279108, -4744356.41124122, 3752863.1916666874, + 6.2, -2001789.9941929616, -4744354.766142638, 3752863.886542615, + 6.21, -2001790.0893105536, -4744353.113739202, 3752864.5830159476, + 6.22, -2001790.1814698034, -4744351.4539543325, 3752865.2811349584, + 6.23, -2001790.2706598283, -4744349.786711458, 3752865.9809479206, + 6.24, -2001790.3568697446, -4744348.111934002, 3752866.6825031047, + 6.25, -2001790.4400886674, -4744346.429545386, 3752867.3858487825, + 6.26, -2001790.5203057139, -4744344.739469034, 3752868.091033227, + 6.2700000000000005, -2001790.5975100002, -4744343.041628374, 3752868.798104711, + 6.28, -2001790.6716906424, -4744341.335946825, 3752869.5071115047, + 6.29, -2001790.7428367576, -4744339.622347817, 3752870.2181018814, + 6.3, -2001790.8109374605, -4744337.900754769, 3752870.9311241126, + 6.3100000000000005, -2001790.8759818687, -4744336.171091108, 3752871.646226471, + 6.32, -2001790.9379590987, -4744334.433280257, 3752872.3634572285, + 6.33, -2001790.9968582657, -4744332.687245638, 3752873.082864656, + 6.34, -2001791.0526684872, -4744330.932910679, 3752873.8044970282, + 6.3500000000000005, -2001791.1053788788, -4744329.1701988, 3752874.5284026144, + 6.36, -2001791.154978557, -4744327.39903343, 3752875.2546296883, + 6.37, -2001791.201456638, -4744325.619337989, 3752875.983226522, + 6.38, -2001791.244802238, -4744323.831035903, 3752876.7142413864, + 6.390000000000001, -2001791.285004473, -4744322.034050594, 3752877.447722554, + 6.4, -2001791.3220524606, -4744320.228305489, 3752878.183718298, + 6.41, -2001791.3559353156, -4744318.413724009, 3752878.922276889, + 6.42, -2001791.3866421557, -4744316.590229581, 3752879.6634465996, + 6.43, -2001791.4141620966, -4744314.757745627, 3752880.4072757033, + 6.44, -2001791.4384842534, -4744312.916195571, 3752881.1538124694, + 6.45, -2001791.4595977443, -4744311.065502839, 3752881.903105172, + 6.46, -2001791.4774916845, -4744309.205590854, 3752882.655202083, + 6.47, -2001791.4921551906, -4744307.33638304, 3752883.4101514723, + 6.48, -2001791.503577379, -4744305.45780282, 3752884.168001616, + 6.49, -2001791.5117473663, -4744303.569773621, 3752884.928800783, + 6.5, -2001791.516654268, -4744301.672218864, 3752885.6925972463, + 6.51, -2001791.5182872007, -4744299.765061974, 3752886.4594392776, + 6.5200000000000005, -2001791.516635281, -4744297.848226375, 3752887.22937515, + 6.53, -2001791.5116876247, -4744295.921635492, 3752888.0024531344, + 6.54, -2001791.5034333493, -4744293.985212748, 3752888.778721505, + 6.55, -2001791.49186157, -4744292.038881569, 3752889.558228531, + 6.5600000000000005, -2001791.4769614034, -4744290.0825653775, 3752890.341022486, + 6.57, -2001791.458721965, -4744288.116187595, 3752891.127151641, + 6.58, -2001791.4371323725, -4744286.13967165, 3752891.9166642693, + 6.59, -2001791.4121817413, -4744284.152940964, 3752892.7096086433, + 6.6000000000000005, -2001791.3838591883, -4744282.155918963, 3752893.506033034, + 6.61, -2001791.3521538295, -4744280.1485290695, 3752894.305985715, + 6.62, -2001791.3170547811, -4744278.130694709, 3752895.109514956, + 6.63, -2001791.2785511592, -4744276.102339302, 3752895.9166690297, + 6.640000000000001, -2001791.236632081, -4744274.063386278, 3752896.7274962105, + 6.65, -2001791.1912866617, -4744272.013759056, 3752897.5420447667, + 6.66, -2001791.1425040183, -4744269.953381063, 3752898.3603629735, + 6.67, -2001791.0902732671, -4744267.882175723, 3752899.182499103, + 6.68, -2001791.034583524, -4744265.800066458, 3752900.0085014254, + 6.69, -2001790.9754239055, -4744263.706976695, 3752900.8384182127, + 6.7, -2001790.9127835277, -4744261.602829854, 3752901.672297738, + 6.71, -2001790.8466515082, -4744259.487549366, 3752902.510188274, + 6.72, -2001790.777016962, -4744257.361058649, 3752903.352138093, + 6.73, -2001790.703869005, -4744255.2232811265, 3752904.1981954644, + 6.74, -2001790.6271967539, -4744253.074140226, 3752905.048408662, + 6.75, -2001790.5469893261, -4744250.91355937, 3752905.902825958, + 6.76, -2001790.4632358365, -4744248.741461984, 3752906.761495624, + 6.7700000000000005, -2001790.375925403, -4744246.557771492, 3752907.624465935, + 6.78, -2001790.2850471397, -4744244.362411315, 3752908.491785157, + 6.79, -2001790.1905901649, -4744242.155304878, 3752909.363501567, + 6.8, -2001790.0925435943, -4744239.9363756105, 3752910.239663436, + 6.8100000000000005, -2001789.9908965426, -4744237.705546928, 3752911.120319034, + 6.82, -2001789.8856381287, -4744235.462742261, 3752912.005516637, + 6.83, -2001789.7767574685, -4744233.2078850325, 3752912.8953045146, + 6.84, -2001789.6642436762, -4744230.940898663, 3752913.7897309386, + 6.8500000000000005, -2001789.5480858698, -4744228.661706581, 3752914.688844181, + 6.86, -2001789.4282731651, -4744226.370232207, 3752915.5926925144, + 6.87, -2001789.3047946782, -4744224.066398967, 3752916.5013242112, + 6.88, -2001789.1776395265, -4744221.7501302855, 3752917.4147875435, + 6.890000000000001, -2001789.0467968257, -4744219.421349588, 3752918.3331307834, + 6.9, -2001788.9122556911, -4744217.079980292, 3752919.256402202, + 6.91, -2001788.7740052405, -4744214.725945828, 3752920.1846500724, + 6.92, -2001788.6320345898, -4744212.35916962, 3752921.117922667, + 6.93, -2001788.4864809366, -4744209.976717252, 3752922.055688722, + 6.94, -2001788.337780564, -4744207.570156753, 3752922.996241048, + 6.95, -2001788.1859653976, -4744205.139151266, 3752923.939449168, + 6.96, -2001788.0310383285, -4744202.683933324, 3752924.8852960533, + 6.97, -2001787.8730022493, -4744200.204735457, 3752925.833764673, + 6.98, -2001787.711860052, -4744197.701790199, 3752926.7848379994, + 6.99, -2001787.5476146284, -4744195.175330079, 3752927.738498999, + 7, -2001787.3802688706, -4744192.625587633, 3752928.6947306436, + 7.01, -2001787.209825671, -4744190.052795391, 3752929.653515903, + 7.0200000000000005, -2001787.036287921, -4744187.457185883, 3752930.614837746, + 7.03, -2001786.8596585125, -4744184.838991646, 3752931.578679145, + 7.04, -2001786.6799403385, -4744182.198445207, 3752932.545023068, + 7.05, -2001786.4971362904, -4744179.535779101, 3752933.513852485, + 7.0600000000000005, -2001786.31124926, -4744176.851225859, 3752934.485150367, + 7.07, -2001786.1222821395, -4744174.145018011, 3752935.4588996833, + 7.08, -2001785.9302378213, -4744171.417388093, 3752936.4350834037, + 7.09, -2001785.7351191966, -4744168.6685686335, 3752937.4136844985, + 7.1000000000000005, -2001785.5369291585, -4744165.898792167, 3752938.394685938, + 7.11, -2001785.3356705979, -4744163.108291224, 3752939.3780706916, + 7.12, -2001785.1313464076, -4744160.2972983355, 3752940.363821729, + 7.13, -2001784.9239594792, -4744157.466046038, 3752941.3519220212, + 7.140000000000001, -2001784.7135127052, -4744154.614766859, 3752942.3423545375, + 7.15, -2001784.500008977, -4744151.743693331, 3752943.3351022475, + 7.16, -2001784.2834511872, -4744148.853057987, 3752944.330148122, + 7.17, -2001784.063842227, -4744145.9430933595, 3752945.32747513, + 7.18, -2001783.8411849895, -4744143.014031979, 3752946.327066242, + 7.19, -2001783.6154823657, -4744140.06610638, 3752947.3289044285, + 7.2, -2001783.3867372482, -4744137.099549091, 3752948.332972659, + 7.21, -2001783.1549525291, -4744134.114592648, 3752949.3392539024, + 7.22, -2001782.9201311, -4744131.111469579, 3752950.34773113, + 7.23, -2001782.682275853, -4744128.090412417, 3752951.3583873115, + 7.24, -2001782.4413896804, -4744125.051653697, 3752952.371205417, + 7.25, -2001782.1974754743, -4744121.995425948, 3752953.3861684157, + 7.26, -2001781.950536126, -4744118.921961702, 3752954.4032592787, + 7.2700000000000005, -2001781.700574528, -4744115.831493492, 3752955.422460975, + 7.28, -2001781.4475935728, -4744112.724253851, 3752956.443756474, + 7.29, -2001781.1915961516, -4744109.600475308, 3752957.467128748, + 7.3, -2001780.9325851568, -4744106.460390397, 3752958.492560765, + 7.3100000000000005, -2001780.6705634804, -4744103.304231652, 3752959.520035495, + 7.32, -2001780.4055340139, -4744100.132231601, 3752960.5495359083, + 7.33, -2001780.1374996505, -4744096.9446227765, 3752961.5810449757, + 7.34, -2001779.8664632807, -4744093.741637712, 3752962.6145456666, + 7.3500000000000005, -2001779.5924277978, -4744090.523508941, 3752963.650020951, + 7.36, -2001779.3153960933, -4744087.290468993, 3752964.687453797, + 7.37, -2001779.0353710596, -4744084.0427504, 3752965.7268271777, + 7.38, -2001778.752355588, -4744080.780585696, 3752966.768124061, + 7.390000000000001, -2001778.4663525708, -4744077.50420741, 3752967.8113274183, + 7.4, -2001778.1773649, -4744074.213848076, 3752968.8564202175, + 7.41, -2001777.885395468, -4744070.909740226, 3752969.9033854306, + 7.42, -2001777.5904471665, -4744067.592116391, 3752970.9522060254, + 7.43, -2001777.2925228872, -4744064.261209104, 3752972.0028649746, + 7.44, -2001776.991625523, -4744060.917250898, 3752973.055345247, + 7.45, -2001776.6877579652, -4744057.560474301, 3752974.1096298113, + 7.46, -2001776.3809231056, -4744054.19111185, 3752975.165701639, + 7.47, -2001776.0711238375, -4744050.809396073, 3752976.223543699, + 7.48, -2001775.7583630509, -4744047.415559504, 3752977.283138962, + 7.49, -2001775.4426436399, -4744044.009834675, 3752978.344470398, + 7.5, -2001775.123968495, -4744040.592454117, 3752979.4075209764, + 7.51, -2001774.802340509, -4744037.163650362, 3752980.4722736673, + 7.5200000000000005, -2001774.4777625736, -4744033.723655944, 3752981.5387114417, + 7.53, -2001774.1502375815, -4744030.272703392, 3752982.6068172683, + 7.54, -2001773.8197684232, -4744026.8110252395, 3752983.676574117, + 7.55, -2001773.486357992, -4744023.3388540195, 3752984.7479649587, + 7.5600000000000005, -2001773.1500091795, -4744019.856422263, 3752985.820972762, + 7.57, -2001772.8107248782, -4744016.363962501, 3752986.8955804994, + 7.58, -2001772.468507979, -4744012.861707267, 3752987.9717711373, + 7.59, -2001772.123361375, -4744009.349889092, 3752989.0495276484, + 7.6000000000000005, -2001771.7752879576, -4744005.828740509, 3752990.1288330024, + 7.61, -2001771.4242906198, -4744002.298494049, 3752991.2096701683, + 7.62, -2001771.070372252, -4743998.759382246, 3752992.2920221165, + 7.63, -2001770.7135357477, -4743995.211637629, 3752993.375871816, + 7.640000000000001, -2001770.3537839982, -4743991.655492731, 3752994.4612022387, + 7.65, -2001769.9911198951, -4743988.091180086, 3752995.5479963524, + 7.66, -2001769.6255463315, -4743984.518932224, 3752996.6362371296, + 7.67, -2001769.2570661986, -4743980.938981676, 3752997.7259075376, + 7.68, -2001768.8856823882, -4743977.351560975, 3752998.816990548, + 7.69, -2001768.5113977934, -4743973.756902656, 3752999.9094691304, + 7.7, -2001768.1342153053, -4743970.155239246, 3753001.0033262554, + 7.71, -2001767.7541378164, -4743966.546803281, 3753002.0985448915, + 7.72, -2001767.3711682183, -4743962.931827289, 3753003.195108009, + 7.73, -2001766.9853094036, -4743959.310543806, 3753004.2929985793, + 7.74, -2001766.5965642636, -4743955.683185362, 3753005.3921995703, + 7.75, -2001766.2049356906, -4743952.04998449, 3753006.4926939537, + 7.76, -2001765.8104265772, -4743948.41117372, 3753007.5944646993, + 7.7700000000000005, -2001765.4130398142, -4743944.766985586, 3753008.697494776, + 7.78, -2001765.012778295, -4743941.117652618, 3753009.8017671537, + 7.79, -2001764.6096449108, -4743937.463407352, 3753010.9072648035, + 7.8, -2001764.2036425539, -4743933.804482317, 3753012.0139706945, + 7.8100000000000005, -2001763.7947741153, -4743930.141110042, 3753013.121867797, + 7.82, -2001763.3830424887, -4743926.4735230645, 3753014.230939082, + 7.83, -2001762.9684505651, -4743922.801953914, 3753015.341167517, + 7.84, -2001762.5510012368, -4743919.126635123, 3753016.4525360735, + 7.8500000000000005, -2001762.1306973954, -4743915.447799223, 3753017.5650277226, + 7.86, -2001761.7075419337, -4743911.765678746, 3753018.6786254314, + 7.87, -2001761.2815377433, -4743908.080506224, 3753019.7933121724, + 7.88, -2001760.8526877158, -4743904.39251419, 3753020.9090709137, + 7.890000000000001, -2001760.4209947437, -4743900.701935174, 3753022.0258846274, + 7.9, -2001759.9864617188, -4743897.0090017095, 3753023.143736281, + 7.91, -2001759.5490915338, -4743893.313946328, 3753024.2626088466, + 7.92, -2001759.10888708, -4743889.617001561, 3753025.3824852933, + 7.930000000000001, -2001758.665851249, -4743885.918399942, 3753026.5033485903, + 7.94, -2001758.2199869337, -4743882.218374003, 3753027.625181709, + 7.95, -2001757.7712970264, -4743878.517156275, 3753028.7479676176, + 7.96, -2001757.3197844175, -4743874.81497929, 3753029.871689288, + 7.97, -2001756.8654520006, -4743871.112075578, 3753030.9963296885, + 7.98, -2001756.408302667, -4743867.408677675, 3753032.1218717904, + 7.99, -2001755.9483393093, -4743863.705018111, 3753033.2482985626, + 8, -2001755.4855648184, -4743860.001329418, 3753034.375592976, + 8.01, -2001755.0199820877, -4743856.297844127, 3753035.503738, + 8.02, -2001754.5515940078, -4743852.594794771, 3753036.6327166045, + 8.03, -2001754.080403472, -4743848.8924138835, 3753037.762511759, + 8.040000000000001, -2001753.6064133714, -4743845.190933993, 3753038.8931064345, + 8.05, -2001753.1296265984, -4743841.490587636, 3753040.0244836006, + 8.06, -2001752.650046045, -4743837.791607341, 3753041.156626228, + 8.07, -2001752.1676746034, -4743834.094225641, 3753042.2895172844, + 8.08, -2001751.6825151658, -4743830.398675067, 3753043.423139742, + 8.09, -2001751.1945706229, -4743826.705188152, 3753044.55747657, + 8.1, -2001750.7038438679, -4743823.01399743, 3753045.692510738, + 8.11, -2001750.2103377925, -4743819.325335429, 3753046.8282252164, + 8.120000000000001, -2001749.7140552895, -4743815.639434683, 3753047.964602975, + 8.13, -2001749.2149992504, -4743811.956527727, 3753049.1016269843, + 8.14, -2001748.713172566, -4743808.276847086, 3753050.239280213, + 8.15, -2001748.2085781298, -4743804.600625298, 3753051.377545632, + 8.16, -2001747.701218833, -4743800.928094892, 3753052.516406211, + 8.17, -2001747.1910975683, -4743797.259488401, 3753053.655844921, + 8.18, -2001746.6782172266, -4743793.595038356, 3753054.7958447295, + 8.19, -2001746.162580702, -4743789.934977291, 3753055.936388609, + 8.2, -2001745.6441908844, -4743786.279537736, 3753057.077459528, + 8.21, -2001745.1230506666, -4743782.628952226, 3753058.2190404567, + 8.22, -2001744.599162941, -4743778.98345329, 3753059.361114366, + 8.23, -2001744.0725305993, -4743775.343273459, 3753060.503664225, + 8.24, -2001743.5431565335, -4743771.708645269, 3753061.6466730027, + 8.25, -2001743.0110436354, -4743768.079801249, 3753062.7901236713, + 8.26, -2001742.476194797, -4743764.456973931, 3753063.933999199, + 8.27, -2001741.9386129112, -4743760.840395848, 3753065.078282557, + 8.28, -2001741.3983008687, -4743757.230299532, 3753066.2229567133, + 8.290000000000001, -2001740.8552615624, -4743753.626917516, 3753067.3680046406, + 8.3, -2001740.309497884, -4743750.03048233, 3753068.5134093063, + 8.31, -2001739.7610127255, -4743746.441226507, 3753069.6591536817, + 8.32, -2001739.2098089796, -4743742.859382579, 3753070.805220737, + 8.33, -2001738.6558895372, -4743739.285183077, 3753071.951593442, + 8.34, -2001738.0992572906, -4743735.718860532, 3753073.0982547654, + 8.35, -2001737.5399151328, -4743732.160647481, 3753074.2451876784, + 8.36, -2001736.9778659546, -4743728.6107764505, 3753075.392375151, + 8.370000000000001, -2001736.4131126488, -4743725.069479975, 3753076.539800153, + 8.38, -2001735.8456581063, -4743721.536990588, 3753077.6874456527, + 8.39, -2001735.275505221, -4743718.013540817, 3753078.835294624, + 8.4, -2001734.702656883, -4743714.499363199, 3753079.9833300323, + 8.41, -2001734.1271159858, -4743710.994690264, 3753081.1315348516, + 8.42, -2001733.5488854207, -4743707.499754541, 3753082.2798920483, + 8.43, -2001732.9679680795, -4743704.014788566, 3753083.4283845956, + 8.44, -2001732.3843668543, -4743700.540024869, 3753084.576995461, + 8.45, -2001731.7980846376, -4743697.075695983, 3753085.725707614, + 8.46, -2001731.2091243214, -4743693.622034441, 3753086.874504028, + 8.47, -2001730.6174887968, -4743690.179272772, 3753088.0233676694, + 8.48, -2001730.0231809577, -4743686.747643511, 3753089.1722815107, + 8.49, -2001729.4262036937, -4743683.327379187, 3753090.32122852, + 8.5, -2001728.8265598982, -4743679.918712334, 3753091.470191668, + 8.51, -2001728.2242524636, -4743676.521875484, 3753092.619153925, + 8.52, -2001727.6192842806, -4743673.137101168, 3753093.7680982603, + 8.53, -2001727.0116582424, -4743669.764621918, 3753094.917007645, + 8.540000000000001, -2001726.401377241, -4743666.404670268, 3753096.0658650477, + 8.55, -2001725.7884441668, -4743663.057478747, 3753097.214653438, + 8.56, -2001725.172861914, -4743659.72327989, 3753098.3633557884, + 8.57, -2001724.5546333736, -4743656.402306229, 3753099.5119550675, + 8.58, -2001723.9337614374, -4743653.094790291, 3753100.6604342437, + 8.59, -2001723.310248997, -4743649.8009646125, 3753101.8087762888, + 8.6, -2001722.6840989464, -4743646.521061726, 3753102.9569641724, + 8.61, -2001722.0553141753, -4743643.255314159, 3753104.1049808636, + 8.620000000000001, -2001721.423897577, -4743640.00395445, 3753105.2528093336, + 8.63, -2001720.7898520438, -4743636.767215126, 3753106.400432552, + 8.64, -2001720.1531804663, -4743633.54532872, 3753107.5478334874, + 8.65, -2001719.5138857374, -4743630.338527764, 3753108.6949951123, + 8.66, -2001718.8719707492, -4743627.147044791, 3753109.8419003948, + 8.67, -2001718.2274383937, -4743623.971112332, 3753110.988532305, + 8.68, -2001717.580291563, -4743620.810962921, 3753112.1348738135, + 8.69, -2001716.9305331483, -4743617.666829087, 3753113.2809078903, + 8.700000000000001, -2001716.2781660426, -4743614.538943363, 3753114.426617505, + 8.71, -2001715.623193138, -4743611.427538284, 3753115.5719856275, + 8.72, -2001714.9656173254, -4743608.332846376, 3753116.716995227, + 8.73, -2001714.3054414978, -4743605.255100176, 3753117.861629275, + 8.74, -2001713.642668547, -4743602.194532214, 3753119.0058707413, + 8.75, -2001712.9773013652, -4743599.151375023, 3753120.149702595, + 8.76, -2001712.3093428437, -4743596.125861134, 3753121.2931078067, + 8.77, -2001711.6387958748, -4743593.118223079, 3753122.4360693456, + 8.78, -2001710.9656633511, -4743590.128693392, 3753123.5785701815, + 8.790000000000001, -2001710.2899481636, -4743587.1575046, 3753124.7205932857, + 8.8, -2001709.6116532055, -4743584.204889241, 3753125.862121627, + 8.81, -2001708.930781368, -4743581.271079843, 3753127.0031381766, + 8.82, -2001708.2473355434, -4743578.356308941, 3753128.1436259034, + 8.83, -2001707.5613186231, -4743575.460809062, 3753129.2835677764, + 8.84, -2001706.8727334999, -4743572.584812743, 3753130.422946768, + 8.85, -2001706.1815830662, -4743569.728552514, 3753131.5617458466, + 8.86, -2001705.4878702126, -4743566.892260907, 3753132.6999479826, + 8.870000000000001, -2001704.7915978325, -4743564.076170456, 3753133.8375361455, + 8.88, -2001704.092768817, -4743561.280513689, 3753134.9744933057, + 8.89, -2001703.391386059, -4743558.505523142, 3753136.110802434, + 8.9, -2001702.6874524492, -4743555.751431344, 3753137.246446498, + 8.91, -2001701.980970881, -4743553.0184708275, 3753138.3814084707, + 8.92, -2001701.2719442458, -4743550.306874127, 3753139.5156713193, + 8.93, -2001700.5603754355, -4743547.616873773, 3753140.649218016, + 8.94, -2001699.8462673416, -4743544.948702295, 3753141.7820315277, + 8.950000000000001, -2001699.1296228576, -4743542.302592228, 3753142.914094828, + 8.96, -2001698.4104448743, -4743539.678776104, 3753144.045390885, + 8.97, -2001697.6887362842, -4743537.077486454, 3753145.175902669, + 8.98, -2001696.964499979, -4743534.4989558095, 3753146.305613149, + 8.99, -2001696.2377388505, -4743531.943416702, 3753147.4345052955, + 9, -2001695.5084557922, -4743529.411101666, 3753148.5625620796, + 9.01, -2001694.7766536942, -4743526.902243231, 3753149.6897664703, + 9.02, -2001694.0423354497, -4743524.4170739325, 3753150.8161014374, + 9.03, -2001693.3055039507, -4743521.955826298, 3753151.941549952, + 9.040000000000001, -2001692.5661620882, -4743519.518732861, 3753153.066094982, + 9.05, -2001691.8243127556, -4743517.106026157, 3753154.189719499, + 9.06, -2001691.0799588435, -4743514.717938712, 3753155.3124064724, + 9.07, -2001690.3331032454, -4743512.354703062, 3753156.434138872, + 9.08, -2001689.5837488524, -4743510.01655174, 3753157.55489967, + 9.09, -2001688.831898556, -4743507.703717271, 3753158.674671831, + 9.1, -2001688.0775552497, -4743505.416432197, 3753159.7934383317, + 9.11, -2001687.3207218244, -4743503.154929044, 3753160.911182137, + 9.120000000000001, -2001686.5614011725, -4743500.919440344, 3753162.0278862184, + 9.13, -2001685.7995961853, -4743498.710198629, 3753163.143533545, + 9.14, -2001685.0353097557, -4743496.527436432, 3753164.2581070885, + 9.15, -2001684.2685447766, -4743494.371386287, 3753165.3715898204, + 9.16, -2001683.499304138, -4743492.242280723, 3753166.483964706, + 9.17, -2001682.7275907327, -4743490.140352272, 3753167.595214719, + 9.18, -2001681.9534074534, -4743488.065833468, 3753168.705322827, + 9.19, -2001681.1767571906, -4743486.018956843, 3753169.8142720014, + 9.200000000000001, -2001680.3976428383, -4743483.999954928, 3753170.9220452122, + 9.21, -2001679.6160672875, -4743482.009060253, 3753172.0286254287, + 9.22, -2001678.8320334295, -4743480.046505352, 3753173.13399562, + 9.23, -2001678.0455441566, -4743478.112522756, 3753174.2381387576, + 9.24, -2001677.2557244743, -4743476.203254122, 3753175.341785286, + 9.25, -2001676.4614136573, -4743474.3132385155, 3753176.44592696, + 9.26, -2001675.662629569, -4743472.4423387945, 3753177.550563227, + 9.27, -2001674.85939619, -4743470.59044589, 3753178.655688354, + 9.28, -2001674.0517374997, -4743468.75745073, 3753179.761296606, + 9.290000000000001, -2001673.2396774783, -4743466.943244247, 3753180.8673822535, + 9.3, -2001672.4232401066, -4743465.147717371, 3753181.973939559, + 9.31, -2001671.6024493638, -4743463.37076103, 3753183.080962793, + 9.32, -2001670.7773292304, -4743461.612266157, 3753184.1884462214, + 9.33, -2001669.9479036864, -4743459.872123679, 3753185.2963841096, + 9.34, -2001669.1141967124, -4743458.15022453, 3753186.4047707263, + 9.35, -2001668.2762322882, -4743456.446459638, 3753187.5136003373, + 9.36, -2001667.4340343936, -4743454.760719932, 3753188.62286721, + 9.370000000000001, -2001666.5876270088, -4743453.092896344, 3753189.7325656107, + 9.38, -2001665.7370341145, -4743451.442879805, 3753190.8426898075, + 9.39, -2001664.8822796906, -4743449.8105612425, 3753191.953234066, + 9.4, -2001664.0233877164, -4743448.195831589, 3753193.064192653, + 9.41, -2001663.1603821728, -4743446.598581774, 3753194.1755598374, + 9.42, -2001662.2932870402, -4743445.018702727, 3753195.287329884, + 9.43, -2001661.4221262976, -4743443.456085378, 3753196.3994970606, + 9.44, -2001660.5469239263, -4743441.910620658, 3753197.512055633, + 9.450000000000001, -2001659.6677039056, -4743440.382199498, 3753198.6249998696, + 9.46, -2001658.784490216, -4743438.870712825, 3753199.7383240364, + 9.47, -2001657.8973068374, -4743437.376051573, 3753200.8520223997, + 9.48, -2001657.0061777502, -4743435.89810667, 3753201.9660892272, + 9.49, -2001656.1111269342, -4743434.436769048, 3753203.080518787, + 9.5, -2001655.2121783695, -4743432.9919296345, 3753204.1953053437, + 9.51, -2001654.3093560368, -4743431.56347936, 3753205.3104431657, + 9.52, -2001653.402683915, -4743430.151309158, 3753206.425926519, + 9.53, -2001652.4921859857, -4743428.755309954, 3753207.541749671, + 9.540000000000001, -2001651.577886228, -4743427.375372682, 3753208.657906888, + 9.55, -2001650.6598086227, -4743426.011388271, 3753209.7743924377, + 9.56, -2001649.7379771492, -4743424.6632476505, 3753210.891200586, + 9.57, -2001648.8124157875, -4743423.330841749, 3753212.008325601, + 9.58, -2001647.8831485189, -4743422.014061501, 3753213.1257617488, + 9.59, -2001646.9501993223, -4743420.712797835, 3753214.2435032963, + 9.6, -2001646.0135921782, -4743419.426941679, 3753215.361544511, + 9.61, -2001645.073351067, -4743418.156383965, 3753216.4798796587, + 9.620000000000001, -2001644.1294999686, -4743416.901015623, 3753217.598503007, + 9.63, -2001643.1820628631, -4743415.660727584, 3753218.7174088224, + 9.64, -2001642.2310637308, -4743414.435410776, 3753219.836591372, + 9.65, -2001641.2765265515, -4743413.224956132, 3753220.956044924, + 9.66, -2001640.3184753053, -4743412.029254579, 3753222.0757637424, + 9.67, -2001639.3569339726, -4743410.848197049, 3753223.1957420968, + 9.68, -2001638.391926533, -4743409.681674472, 3753224.3159742528, + 9.69, -2001637.4234769673, -4743408.52957778, 3753225.4364544763, + 9.700000000000001, -2001636.451609255, -4743407.391797899, 3753226.5571770365, + 9.71, -2001635.476347377, -4743406.268225762, 3753227.678136198, + 9.72, -2001634.497715313, -4743405.1587523, 3753228.7993262294, + 9.73, -2001633.5157370425, -4743404.06326844, 3753229.920741397, + 9.74, -2001632.5304365468, -4743402.981665116, 3753231.042375968, + 9.75, -2001631.5418378047, -4743401.913833255, 3753232.164224209, + 9.76, -2001630.549964797, -4743400.859663788, 3753233.286280386, + 9.77, -2001629.5548415042, -4743399.819047647, 3753234.408538767, + 9.78, -2001628.5564919056, -4743398.791875759, 3753235.5309936185, + 9.790000000000001, -2001627.5549399818, -4743397.778039056, 3753236.653639207, + 9.8, -2001626.5502097134, -4743396.77742847, 3753237.776469801, + 9.81, -2001625.5423250794, -4743395.7899349285, 3753238.8994796653, + 9.82, -2001624.5313100603, -4743394.815449362, 3753240.022663068, + 9.83, -2001623.5171886368, -4743393.853862701, 3753241.146014276, + 9.84, -2001622.4999847882, -4743392.905065876, 3753242.2695275564, + 9.85, -2001621.4797224952, -4743391.968949816, 3753243.393197174, + 9.86, -2001620.4564257374, -4743391.045405453, 3753244.517017398, + 9.870000000000001, -2001619.4301184954, -4743390.134323717, 3753245.6409824947, + 9.88, -2001618.4008247496, -4743389.235595536, 3753246.76508673, + 9.89, -2001617.3685684786, -4743388.349111842, 3753247.889324372, + 9.9, -2001616.3333736644, -4743387.474763566, 3753249.013689687, + 9.91, -2001615.2952642858, -4743386.612441636, 3753250.1381769427, + 9.92, -2001614.2542643235, -4743385.762036984, 3753251.262780405, + 9.93, -2001613.2103977576, -4743384.923440539, 3753252.3874943405, + 9.94, -2001612.1636885682, -4743384.096543231, 3753253.512313017, + 9.950000000000001, -2001611.114160735, -4743383.281235991, 3753254.6372307017, + 9.96, -2001610.0618382387, -4743382.477409748, 3753255.7622416597, + 9.97, -2001609.006745059, -4743381.684955435, 3753256.8873401596, + 9.98, -2001607.948905176, -4743380.903763979, 3753258.0125204674, + 9.99, -2001606.88834257, -4743380.133726311, 3753259.137776851, + 10, -2001605.8250812213, -4743379.374733363, 3753260.2631035764, + 10.01, -2001604.7591451097, -4743378.626676063, 3753261.3884949097, + 10.02, -2001603.6905582154, -4743377.889445341, 3753262.5139451204, + 10.03, -2001602.6193445185, -4743377.1629321305, 3753263.639448472, + 10.040000000000001, -2001601.5455279988, -4743376.447027356, 3753264.7649992337, + 10.05, -2001600.4691326376, -4743375.741621954, 3753265.8905916717, + 10.06, -2001599.3901824136, -4743375.046606851, 3753267.0162200537, + 10.07, -2001598.3087013073, -4743374.361872977, 3753268.141878645, + 10.08, -2001597.2247132992, -4743373.687311263, 3753269.2675617132, + 10.09, -2001596.138242369, -4743373.022812638, 3753270.393263526, + 10.1, -2001595.0493124966, -4743372.368268035, 3753271.518978349, + 10.11, -2001593.9579476635, -4743371.723568383, 3753272.64470045, + 10.120000000000001, -2001592.8641718484, -4743371.0886046095, 3753273.770424096, + 10.13, -2001591.7680090314, -4743370.463267649, 3753274.896143552, + 10.14, -2001590.669483194, -4743369.847448428, 3753276.0218530875, + 10.15, -2001589.5686183146, -4743369.241037879, 3753277.147546967, + 10.16, -2001588.4654383739, -4743368.6439269325, 3753278.2732194597, + 10.17, -2001587.3599673526, -4743368.056006515, 3753279.3988648313, + 10.18, -2001586.2522292302, -4743367.477167562, 3753280.524477348, + 10.19, -2001585.1422479874, -4743366.907300999, 3753281.650051279, + 10.200000000000001, -2001584.0300476039, -4743366.34629776, 3753282.775580887, + 10.21, -2001582.9156520595, -4743365.794048771, 3753283.9010604434, + 10.22, -2001581.799085335, -4743365.2504449645, 3753285.026484213, + 10.23, -2001580.6803714097, -4743364.715377272, 3753286.151846462, + 10.24, -2001579.5595342643, -4743364.188736621, 3753287.2771414584, + 10.25, -2001578.436597879, -4743363.670413944, 3753288.40236347, + 10.26, -2001577.311586233, -4743363.16030017, 3753289.527506761, + 10.27, -2001576.1845233084, -4743362.658286229, 3753290.6525656004, + 10.28, -2001575.0554330836, -4743362.164263053, 3753291.7775342544, + 10.290000000000001, -2001573.9243395384, -4743361.678121568, 3753292.9024069896, + 10.3, -2001572.7912666542, -4743361.199752709, 3753294.0271780724, + 10.31, -2001571.6562384106, -4743360.729047402, 3753295.1518417723, + 10.32, -2001570.5192787875, -4743360.26589658, 3753296.2763923532, + 10.33, -2001569.380411765, -4743359.810191172, 3753297.400824083, + 10.34, -2001568.2396613231, -4743359.36182211, 3753298.5251312293, + 10.35, -2001567.097051443, -4743358.920680322, 3753299.649308058, + 10.36, -2001565.9526061038, -4743358.486656738, 3753300.7733488376, + 10.370000000000001, -2001564.806349286, -4743358.059642291, 3753301.8972478337, + 10.38, -2001563.6583049695, -4743357.639527908, 3753303.0209993115, + 10.39, -2001562.5084971348, -4743357.22620452, 3753304.1445975406, + 10.4, -2001561.3569497608, -4743356.819563057, 3753305.268036787, + 10.41, -2001560.203686829, -4743356.419494452, 3753306.3913113177, + 10.42, -2001559.048732319, -4743356.025889632, 3753307.514415399, + 10.43, -2001557.8921102106, -4743355.638639526, 3753308.6373432977, + 10.44, -2001556.7338444842, -4743355.25763507, 3753309.7600892824, + 10.450000000000001, -2001555.5739591205, -4743354.882767188, 3753310.8826476177, + 10.46, -2001554.4124780986, -4743354.513926813, 3753312.005012571, + 10.47, -2001553.2494253991, -4743354.151004875, 3753313.127178411, + 10.48, -2001552.0848250026, -4743353.793892304, 3753314.2491394025, + 10.49, -2001550.918700888, -4743353.4424800305, 3753315.370889814, + 10.5, -2001549.7510770366, -4743353.096658984, 3753316.4924239106, + 10.51, -2001548.5819774275, -4743352.756320094, 3753317.61373596, + 10.52, -2001547.4114260415, -4743352.421354294, 3753318.7348202304, + 10.53, -2001546.2394468584, -4743352.09165251, 3753319.8556709867, + 10.540000000000001, -2001545.0660638588, -4743351.767105675, 3753320.9762824965, + 10.55, -2001543.8913010224, -4743351.447604718, 3753322.0966490265, + 10.56, -2001542.715182329, -4743351.133040568, 3753323.2167648436, + 10.57, -2001541.5377317604, -4743350.823304159, 3753324.3366242163, + 10.58, -2001540.3589732938, -4743350.518286417, 3753325.4562214087, + 10.59, -2001539.1789309117, -4743350.217878276, 3753326.5755506903, + 10.6, -2001537.997628593, -4743349.921970662, 3753327.6946063256, + 10.61, -2001536.8150903187, -4743349.630454507, 3753328.813382583, + 10.620000000000001, -2001535.6313400688, -4743349.343220742, 3753329.9318737295, + 10.63, -2001534.446401822, -4743349.060160296, 3753331.0500740297, + 10.64, -2001533.2602995601, -4743348.7811641, 3753332.1679777545, + 10.65, -2001532.073057263, -4743348.506123086, 3753333.285579168, + 10.66, -2001530.8846989092, -4743348.23492818, 3753334.4028725373, + 10.67, -2001529.6952484811, -4743347.967470316, 3753335.5198521297, + 10.68, -2001528.5047299569, -4743347.703640421, 3753336.6365122115, + 10.69, -2001527.3131673182, -4743347.443329427, 3753337.7528470503, + 10.700000000000001, -2001526.1205845443, -4743347.186428266, 3753338.8688509134, + 10.71, -2001524.927005615, -4743346.932827861, 3753339.984518065, + 10.72, -2001523.7324545113, -4743346.682419152, 3753341.0998427765, + 10.73, -2001522.5369552132, -4743346.435093063, 3753342.214819312, + 10.74, -2001521.3405317003, -4743346.190740524, 3753343.329441937, + 10.75, -2001520.1432079526, -4743345.9492524685, 3753344.4437049218, + 10.76, -2001518.9450079503, -4743345.710519823, 3753345.5576025313, + 10.77, -2001517.7459556747, -4743345.474433522, 3753346.671129032, + 10.78, -2001516.5460751047, -4743345.240884491, 3753347.7842786917, + 10.790000000000001, -2001515.3453902197, -4743345.009763665, 3753348.897045777, + 10.8, -2001514.1439250018, -4743344.780961969, 3753350.009424555, + 10.81, -2001512.94170343, -4743344.554370336, 3753351.121409291, + 10.82, -2001511.7387494843, -4743344.329879697, 3753352.232994255, + 10.83, -2001510.5350871447, -4743344.107380982, 3753353.3441737117, + 10.84, -2001509.3307403922, -4743343.886765118, 3753354.454941928, + 10.85, -2001508.1257332054, -4743343.667923038, 3753355.565293171, + 10.86, -2001506.9200895664, -4743343.450745672, 3753356.6752217077, + 10.870000000000001, -2001505.7138334538, -4743343.235123951, 3753357.7847218057, + 10.88, -2001504.506988848, -4743343.020948802, 3753358.8937877305, + 10.89, -2001503.29957973, -4743342.808111159, 3753360.00241375, + 10.9, -2001502.0916300784, -4743342.596501948, 3753361.110594131, + 10.91, -2001500.8831638745, -4743342.386012103, 3753362.21832314, + 10.92, -2001499.6742050976, -4743342.1765325535, 3753363.3255950445, + 10.93, -2001498.464777729, -4743341.967954228, 3753364.43240411, + 10.94, -2001497.2549057475, -4743341.760168058, 3753365.5387446056, + 10.950000000000001, -2001496.0446131343, -4743341.553064972, 3753366.644610796, + 10.96, -2001494.8339238686, -4743341.346535903, 3753367.749996949, + 10.97, -2001493.6228619306, -4743341.140471778, 3753368.854897332, + 10.98, -2001492.411451301, -4743340.93476353, 3753369.9593062107, + 10.99, -2001491.19971596, -4743340.729302088, 3753371.063217853, + 11, -2001489.987679887, -4743340.523978381, 3753372.1666265256, + 11.01, -2001488.7753670625, -4743340.31868334, 3753373.269526495, + 11.02, -2001487.5628014663, -4743340.113307896, 3753374.371912028, + 11.03, -2001486.3500070788, -4743339.907742981, 3753375.4737773924, + 11.040000000000001, -2001485.1370078805, -4743339.701879519, 3753376.5751168528, + 11.05, -2001483.9238278507, -4743339.495608446, 3753377.6759246793, + 11.06, -2001482.7104909702, -4743339.28882069, 3753378.776195137, + 11.07, -2001481.4970212183, -4743339.08140718, 3753379.8759224922, + 11.08, -2001480.2834425757, -4743338.873258849, 3753380.975101013, + 11.09, -2001479.0697790233, -4743338.6642666245, 3753382.0737249656, + 11.1, -2001477.8560545396, -4743338.454321438, 3753383.171788618, + 11.11, -2001476.642293106, -4743338.243314221, 3753384.269286236, + 11.120000000000001, -2001475.4285187013, -4743338.031135902, 3753385.3662120863, + 11.13, -2001474.214755307, -4743337.817677409, 3753386.4625604353, + 11.14, -2001473.0010269028, -4743337.602829677, 3753387.558325552, + 11.15, -2001471.7873574677, -4743337.386483633, 3753388.653501701, + 11.16, -2001470.5737709831, -4743337.168530207, 3753389.748083151, + 11.17, -2001469.360291429, -4743336.9488603305, 3753390.842064168, + 11.18, -2001468.1469427855, -4743336.727364935, 3753391.9354390195, + 11.19, -2001466.9337490315, -4743336.503934946, 3753393.0282019703, + 11.200000000000001, -2001465.7207341492, -4743336.278461299, 3753394.1203472903, + 11.21, -2001464.5079221174, -4743336.050834921, 3753395.211869244, + 11.22, -2001463.2953369159, -4743335.820946743, 3753396.3027621, + 11.23, -2001462.0830025254, -4743335.588687693, 3753397.3930201232, + 11.24, -2001460.8709429263, -4743335.353948707, 3753398.4826375823, + 11.25, -2001459.659182098, -4743335.116620708, 3753399.571608744, + 11.26, -2001458.4477440212, -4743334.876594632, 3753400.6599278743, + 11.27, -2001457.2366526758, -4743334.633761406, 3753401.747589241, + 11.28, -2001456.0259320417, -4743334.388011961, 3753402.8345871097, + 11.290000000000001, -2001454.815606099, -4743334.139237228, 3753403.9209157485, + 11.3, -2001453.6056988286, -4743333.887328135, 3753405.0065694237, + 11.31, -2001452.3962342092, -4743333.632175612, 3753406.091542402, + 11.32, -2001451.1872362227, -4743333.373670595, 3753407.1758289514, + 11.33, -2001449.9787288478, -4743333.111704008, 3753408.259423338, + 11.34, -2001448.770736065, -4743332.846166782, 3753409.342319828, + 11.35, -2001447.5632818541, -4743332.576949849, 3753410.424512689, + 11.36, -2001446.356390196, -4743332.303944138, 3753411.5059961877, + 11.370000000000001, -2001445.1500850702, -4743332.027040578, 3753412.5867645913, + 11.38, -2001443.9443904578, -4743331.746130104, 3753413.6668121666, + 11.39, -2001442.739330337, -4743331.461103641, 3753414.74613318, + 11.4, -2001441.5349286892, -4743331.171852122, 3753415.824721899, + 11.41, -2001440.3312094952, -4743330.878266476, 3753416.902572591, + 11.42, -2001439.1281967333, -4743330.580237634, 3753417.979679521, + 11.43, -2001437.9259143851, -4743330.2776565235, 3753419.056036958, + 11.44, -2001436.72438643, -4743329.970414079, 3753420.1316391677, + 11.450000000000001, -2001435.5236368482, -4743329.658401227, 3753421.2064804155, + 11.46, -2001434.3236896205, -4743329.341508901, 3753422.2805549717, + 11.47, -2001433.1245687257, -4743329.0196280265, 3753423.3538571005, + 11.48, -2001431.9262981457, -4743328.69264954, 3753424.426381072, + 11.49, -2001430.7289018582, -4743328.360464365, 3753425.4981211475, + 11.5, -2001429.5324038453, -4743328.022963436, 3753426.5690715997, + 11.51, -2001428.336828086, -4743327.680037682, 3753427.6392266913, + 11.52, -2001427.1421985622, -4743327.331578035, 3753428.7085806928, + 11.53, -2001425.9485392515, -4743326.977475421, 3753429.7771278676, + 11.540000000000001, -2001424.7558621974, -4743326.617740015, 3753430.8448787816, + 11.55, -2001423.5635479514, -4743326.258631781, 3753431.912694144, + 11.56, -2001422.3712286633, -4743325.903725544, 3753432.9810555647, + 11.57, -2001421.1788934444, -4743325.552999445, 3753434.049950868, + 11.58, -2001419.9865314038, -4743325.2064316245, 3753435.11936788, + 11.59, -2001418.794131653, -4743324.864000223, 3753436.1892944253, + 11.6, -2001417.6016833028, -4743324.525683377, 3753437.25971833, + 11.61, -2001416.409175462, -4743324.191459229, 3753438.3306274205, + 11.620000000000001, -2001415.2165972425, -4743323.861305918, 3753439.402009521, + 11.63, -2001414.023937754, -4743323.535201581, 3753440.473852456, + 11.64, -2001412.8311861071, -4743323.213124364, 3753441.5461440533, + 11.65, -2001411.6383314119, -4743322.895052401, 3753442.618872137, + 11.66, -2001410.4453627786, -4743322.580963833, 3753443.6920245313, + 11.67, -2001409.2522693186, -4743322.270836802, 3753444.7655890645, + 11.68, -2001408.0590401415, -4743321.964649445, 3753445.839553559, + 11.69, -2001406.8656643576, -4743321.662379904, 3753446.9139058436, + 11.700000000000001, -2001405.6721310776, -4743321.364006317, 3753447.9886337407, + 11.71, -2001404.4784294122, -4743321.069506824, 3753449.063725077, + 11.72, -2001403.2845484712, -4743320.778859566, 3753450.139167678, + 11.73, -2001402.090477365, -4743320.49204268, 3753451.2149493685, + 11.74, -2001400.8962052043, -4743320.209034309, 3753452.291057974, + 11.75, -2001399.7017210994, -4743319.9298125915, 3753453.3674813206, + 11.76, -2001398.507014161, -4743319.654355666, 3753454.444207234, + 11.77, -2001397.312073499, -4743319.382641674, 3753455.521223539, + 11.78, -2001396.1168882237, -4743319.114648753, 3753456.5985180605, + 11.790000000000001, -2001394.9214474466, -4743318.850355046, 3753457.6760786246, + 11.8, -2001393.7257402765, -4743318.589738689, 3753458.753893057, + 11.81, -2001392.529755825, -4743318.332777823, 3753459.8319491823, + 11.82, -2001391.3334832022, -4743318.079450591, 3753460.9102348266, + 11.83, -2001390.1369115177, -4743317.829735128, 3753461.988737815, + 11.84, -2001388.9400298828, -4743317.583609577, 3753463.067445973, + 11.85, -2001387.7428274076, -4743317.341052076, 3753464.146347126, + 11.86, -2001386.545293203, -4743317.102040766, 3753465.2254291, + 11.870000000000001, -2001385.3474163786, -4743316.866553783, 3753466.3046797197, + 11.88, -2001384.1491860452, -4743316.634569272, 3753467.3840868096, + 11.89, -2001382.950591313, -4743316.406065371, 3753468.463638198, + 11.9, -2001381.751621293, -4743316.181020219, 3753469.543321708, + 11.91, -2001380.5522650946, -4743315.9594119545, 3753470.6231251657, + 11.92, -2001379.3525118288, -4743315.741218718, 3753471.7030363963, + 11.93, -2001378.152350606, -4743315.526418652, 3753472.7830432253, + 11.94, -2001376.9517705361, -4743315.314989892, 3753473.8631334784, + 11.950000000000001, -2001375.7507607301, -4743315.106910582, 3753474.943294981, + 11.96, -2001374.5493102986, -4743314.902158857, 3753476.023515558, + 11.97, -2001373.347408351, -4743314.70071286, 3753477.103783035, + 11.98, -2001372.1450439987, -4743314.502550731, 3753478.184085238, + 11.99, -2001370.9422063513, -4743314.307650607, 3753479.2644099924, + 12, -2001369.73888452, -4743314.11599063, 3753480.3447451233, + 12.01, -2001368.5350676142, -4743313.92754894, 3753481.425078456, + 12.02, -2001367.330744745, -4743313.742303674, 3753482.5053978153, + 12.030000000000001, -2001366.1259050227, -4743313.560232975, 3753483.585691028, + 12.040000000000001, -2001364.9205375577, -4743313.381314981, 3753484.6659459188, + 12.05, -2001363.7146314606, -4743313.205527832, 3753485.7461503134, + 12.06, -2001362.508175841, -4743313.032849667, 3753486.8262920366, + 12.07, -2001361.30115981, -4743312.863258625, 3753487.9063589144, + 12.08, -2001360.0935724778, -4743312.696732851, 3753488.986338773, + 12.09, -2001358.8854029544, -4743312.533250478, 3753490.0662194355, + 12.1, -2001357.6766403513, -4743312.372789649, 3753491.14598873, + 12.11, -2001356.4672737778, -4743312.215328503, 3753492.2256344804, + 12.120000000000001, -2001355.2572923447, -4743312.060845182, 3753493.305144512, + 12.13, -2001354.0466851625, -4743311.909317822, 3753494.3845066507, + 12.14, -2001352.8354413414, -4743311.760724565, 3753495.4637087225, + 12.15, -2001351.6235499915, -4743311.615043551, 3753496.5427385513, + 12.16, -2001350.411000224, -4743311.4722529175, 3753497.621583964, + 12.17, -2001349.1977811486, -4743311.332330807, 3753498.7002327857, + 12.18, -2001347.983881876, -4743311.195255357, 3753499.7786728414, + 12.19, -2001346.7692915166, -4743311.0610047085, 3753500.856891957, + 12.200000000000001, -2001345.553999181, -4743310.929557, 3753501.934877957, + 12.21, -2001344.3379939792, -4743310.800890375, 3753503.012618669, + 12.22, -2001343.1212650212, -4743310.674982966, 3753504.090101915, + 12.23, -2001341.9038014181, -4743310.55181292, 3753505.1673155227, + 12.24, -2001340.6855922805, -4743310.431358373, 3753506.2442473173, + 12.25, -2001339.4666267175, -4743310.313597465, 3753507.3208851255, + 12.26, -2001338.2468938413, -4743310.198508337, 3753508.39721677, + 12.27, -2001337.026382761, -4743310.086069127, 3753509.473230078, + 12.280000000000001, -2001335.8050825873, -4743309.976257976, 3753510.548912874, + 12.290000000000001, -2001334.5829824307, -4743309.869053024, 3753511.6242529843, + 12.3, -2001333.3600714016, -4743309.76443241, 3753512.6992382337, + 12.31, -2001332.1363386107, -4743309.662374272, 3753513.7738564485, + 12.32, -2001330.9117731678, -4743309.562856753, 3753514.848095453, + 12.33, -2001329.6863641832, -4743309.465857991, 3753515.9219430736, + 12.34, -2001328.4601007677, -4743309.371356126, 3753516.995387134, + 12.35, -2001327.2329720322, -4743309.279329299, 3753518.068415462, + 12.36, -2001326.0049670862, -4743309.1897556465, 3753519.141015882, + 12.370000000000001, -2001324.77607504, -4743309.102613311, 3753520.213176219, + 12.38, -2001323.5462850048, -4743309.017880432, 3753521.2848842978, + 12.39, -2001322.3155860908, -4743308.935535148, 3753522.3561279457, + 12.4, -2001321.0839674077, -4743308.8555556, 3753523.4268949875, + 12.41, -2001319.8514180668, -4743308.777919926, 3753524.497173249, + 12.42, -2001318.6179271778, -4743308.702606267, 3753525.566950553, + 12.43, -2001317.3834838513, -4743308.629592763, 3753526.636214729, + 12.44, -2001316.148077198, -4743308.558857553, 3753527.704953599, + 12.450000000000001, -2001314.9116963279, -4743308.490378777, 3753528.77315499, + 12.46, -2001313.6743303519, -4743308.424134575, 3753529.8408067273, + 12.47, -2001312.43596838, -4743308.360103087, 3753530.9078966365, + 12.48, -2001311.1965995221, -4743308.29826245, 3753531.9744125423, + 12.49, -2001309.9562128896, -4743308.238590807, 3753533.0403422713, + 12.5, -2001308.7147975923, -4743308.181066296, 3753534.1056736475, + 12.51, -2001307.4723427403, -4743308.12566706, 3753535.1703944984, + 12.52, -2001306.2288374451, -4743308.072371232, 3753536.234492646, + 12.530000000000001, -2001304.984270816, -4743308.021156959, 3753537.297955919, + 12.540000000000001, -2001303.7386319642, -4743307.972002377, 3753538.3607721413, + 12.55, -2001302.4919099994, -4743307.924885624, 3753539.422929139, + 12.56, -2001301.2440940323, -4743307.879784844, 3753540.4844147367, + 12.57, -2001299.9951731735, -4743307.836678174, 3753541.54521676, + 12.58, -2001298.745136533, -4743307.795543754, 3753542.6053230357, + 12.59, -2001297.4939732214, -4743307.756359725, 3753543.664721388, + 12.6, -2001296.2416723487, -4743307.719104226, 3753544.7233996424, + 12.61, -2001294.988223026, -4743307.683755396, 3753545.7813456245, + 12.620000000000001, -2001293.7336143632, -4743307.650291376, 3753546.838547159, + 12.63, -2001292.4778354715, -4743307.618690303, 3753547.894992072, + 12.64, -2001291.2208754595, -4743307.588930319, 3753548.9506681887, + 12.65, -2001289.9627234393, -4743307.560989566, 3753550.005563336, + 12.66, -2001288.7033685213, -4743307.53484618, 3753551.0596653377, + 12.67, -2001287.4427998143, -4743307.510478302, 3753552.112962019, + 12.68, -2001286.1810064302, -4743307.4878640715, 3753553.165441206, + 12.69, -2001284.917977479, -4743307.466981627, 3753554.2170907245, + 12.700000000000001, -2001283.653702071, -4743307.447809111, 3753555.2678984, + 12.71, -2001282.3881693163, -4743307.4303246625, 3753556.3178520557, + 12.72, -2001281.121368326, -4743307.414506419, 3753557.3669395205, + 12.73, -2001279.8532882098, -4743307.400332523, 3753558.4151486163, + 12.74, -2001278.5839180788, -4743307.3877811115, 3753559.462467172, + 12.75, -2001277.313247042, -4743307.376830327, 3753560.5088830106, + 12.76, -2001276.0412642115, -4743307.367458307, 3753561.554383958, + 12.77, -2001274.7679586965, -4743307.359643194, 3753562.5989578404, + 12.780000000000001, -2001273.4933196083, -4743307.353363125, 3753563.6425924827, + 12.790000000000001, -2001272.2173360565, -4743307.348596239, 3753564.68527571, + 12.8, -2001270.9399971527, -4743307.345320679, 3753565.7269953485, + 12.81, -2001269.6612920053, -4743307.343514582, 3753566.767739223, + 12.82, -2001268.3812097267, -4743307.34315609, 3753567.807495158, + 12.83, -2001267.0997394258, -4743307.34422334, 3753568.846250982, + 12.84, -2001265.8168702135, -4743307.346694475, 3753569.883994517, + 12.85, -2001264.5325912007, -4743307.350547631, 3753570.9207135905, + 12.86, -2001263.246891497, -4743307.3557609515, 3753571.956396028, + 12.870000000000001, -2001261.9597602137, -4743307.362312574, 3753572.9910296537, + 12.88, -2001260.6711864606, -4743307.3701806385, 3753574.0246022926, + 12.89, -2001259.381159348, -4743307.379343284, 3753575.0571017726, + 12.9, -2001258.0896679861, -4743307.389778652, 3753576.088515917, + 12.91, -2001256.7967014862, -4743307.401464881, 3753577.1188325523, + 12.92, -2001255.5022489578, -4743307.414380112, 3753578.1480395026, + 12.93, -2001254.206299512, -4743307.428502483, 3753579.176124595, + 12.94, -2001252.9088422586, -4743307.443810134, 3753580.2030756543, + 12.950000000000001, -2001251.6098663083, -4743307.460281205, 3753581.228880505, + 12.96, -2001250.3093607717, -4743307.477893837, 3753582.2535269735, + 12.97, -2001249.0073147588, -4743307.496626169, 3753583.2770028855, + 12.98, -2001247.7037173798, -4743307.5164563395, 3753584.299296066, + 12.99, -2001246.3985577459, -4743307.537362489, 3753585.3203943404, + 13, -2001245.091824967, -4743307.559322758, 3753586.340285534, + 13.01, -2001243.7835081527, -4743307.582315285, 3753587.3589574723, + 13.02, -2001242.473596415, -4743307.606318211, 3753588.3763979813, + 13.030000000000001, -2001241.1620788628, -4743307.631309673, 3753589.392594885, + 13.040000000000001, -2001239.8489446077, -4743307.657267815, 3753590.4075360107, + 13.05, -2001238.5341827595, -4743307.684170773, 3753591.4212091826, + 13.06, -2001237.2177824283, -4743307.7119966885, 3753592.433602225, + 13.07, -2001235.8997327252, -4743307.740723701, 3753593.444702966, + 13.08, -2001234.5800227607, -4743307.770329952, 3753594.4544992307, + 13.09, -2001233.2586416441, -4743307.800793578, 3753595.4629788427, + 13.1, -2001231.9355784864, -4743307.832092719, 3753596.470129628, + 13.11, -2001230.6108223982, -4743307.864205517, 3753597.4759394126, + 13.120000000000001, -2001229.2843624898, -4743307.89711011, 3753598.4803960226, + 13.13, -2001227.9561878715, -4743307.930784638, 3753599.4834872806, + 13.14, -2001226.6262876538, -4743307.965207242, 3753600.4852010156, + 13.15, -2001225.2946509467, -4743308.0003560595, 3753601.48552505, + 13.16, -2001223.9612668615, -4743308.036209232, 3753602.4844472115, + 13.17, -2001222.6261245077, -4743308.0727449, 3753603.481955325, + 13.18, -2001221.2892129952, -4743308.109941199, 3753604.4780372144, + 13.19, -2001219.950521436, -4743308.147776274, 3753605.472680708, + 13.200000000000001, -2001218.6100389399, -4743308.186228262, 3753606.465873628, + 13.21, -2001217.2677546167, -4743308.225275302, 3753607.457603801, + 13.22, -2001215.9236575768, -4743308.264895536, 3753608.4478590544, + 13.23, -2001214.577736931, -4743308.3050671015, 3753609.4366272097, + 13.24, -2001213.2299817905, -4743308.34576814, 3753610.423896097, + 13.25, -2001211.8803812645, -4743308.386976791, 3753611.4096535384, + 13.26, -2001210.528924463, -4743308.428671192, 3753612.3938873606, + 13.27, -2001209.1756004982, -4743308.470829486, 3753613.376585388, + 13.280000000000001, -2001207.8203984783, -4743308.513429808, 3753614.357735446, + 13.290000000000001, -2001206.4633075155, -4743308.556450305, 3753615.3373253625, + 13.3, -2001205.1043167193, -4743308.59986911, 3753616.31534296, + 13.31, -2001203.743415201, -4743308.6436643675, 3753617.2917760666, + 13.32, -2001202.3805920698, -4743308.687814213, 3753618.266612505, + 13.33, -2001201.0158364363, -4743308.73229679, 3753619.2398401024, + 13.34, -2001199.6491374113, -4743308.777090235, 3753620.2114466834, + 13.35, -2001198.2804841055, -4743308.822172689, 3753621.181420075, + 13.36, -2001196.909865628, -4743308.867522293, 3753622.1497480995, + 13.370000000000001, -2001195.5372710908, -4743308.913117184, 3753623.116418585, + 13.38, -2001194.1626896034, -4743308.958935505, 3753624.0814193552, + 13.39, -2001192.7861102761, -4743309.004955394, 3753625.0447382377, + 13.4, -2001191.40752222, -4743309.051154991, 3753626.0063630557, + 13.41, -2001190.026914545, -4743309.097512436, 3753626.966281638, + 13.42, -2001188.6442763614, -4743309.144005867, 3753627.924481805, + 13.43, -2001187.2595967795, -4743309.190613425, 3753628.8809513855, + 13.44, -2001185.8728649097, -4743309.23731325, 3753629.8356782044, + 13.450000000000001, -2001184.484069863, -4743309.284083482, 3753630.788650087, + 13.46, -2001183.0932007497, -4743309.330902259, 3753631.739854858, + 13.47, -2001181.7002466794, -4743309.377747722, 3753632.6892803432, + 13.48, -2001180.305196763, -4743309.424598012, 3753633.6369143697, + 13.49, -2001178.9080401114, -4743309.4714312665, 3753634.582744761, + 13.5, -2001177.5087658342, -4743309.518225627, 3753635.5267593428, + 13.51, -2001176.1073630424, -4743309.564959232, 3753636.4689459414, + 13.52, -2001174.703820845, -4743309.611610221, 3753637.4092923813, + 13.530000000000001, -2001173.2981283541, -4743309.658156734, 3753638.347786488, + 13.540000000000001, -2001171.8902746798, -4743309.704576911, 3753639.2844160874, + 13.55, -2001170.480248932, -4743309.750848892, 3753640.219169004, + 13.56, -2001169.068040221, -4743309.796950816, 3753641.1520330645, + 13.57, -2001167.6536376574, -4743309.842860824, 3753642.0829960937, + 13.58, -2001166.237030352, -4743309.888557055, 3753643.012045918, + 13.59, -2001164.8182074146, -4743309.934017648, 3753643.9391703606, + 13.6, -2001163.397157956, -4743309.979220744, 3753644.8643572493, + 13.61, -2001161.9738710853, -4743310.02414448, 3753645.787594407, + 13.620000000000001, -2001160.548335915, -4743310.068766999, 3753646.7088696617, + 13.63, -2001159.1205415549, -4743310.11306644, 3753647.628170837, + 13.64, -2001157.6904771142, -4743310.157020942, 3753648.5454857596, + 13.65, -2001156.2581317048, -4743310.2006086465, 3753649.4608022557, + 13.66, -2001154.823494436, -4743310.243807689, 3753650.374108148, + 13.67, -2001153.3865544181, -4743310.286596213, 3753651.2853912627, + 13.68, -2001151.9473007624, -4743310.328952357, 3753652.194639427, + 13.69, -2001150.505722579, -4743310.370854262, 3753653.1018404653, + 13.700000000000001, -2001149.0618089784, -4743310.412280067, 3753654.0069822036, + 13.71, -2001147.6155490698, -4743310.453207908, 3753654.910052464, + 13.72, -2001146.1669319656, -4743310.493615932, 3753655.8110390776, + 13.73, -2001144.7159467745, -4743310.533482272, 3753656.7099298644, + 13.74, -2001143.2625826076, -4743310.572785071, 3753657.606712653, + 13.75, -2001141.8068285757, -4743310.611502469, 3753658.5013752696, + 13.76, -2001140.3486737877, -4743310.649612604, 3753659.393905536, + 13.77, -2001138.8881073557, -4743310.687093616, 3753660.284291281, + 13.780000000000001, -2001137.425118389, -4743310.723923648, 3753661.172520328, + 13.790000000000001, -2001135.9596959988, -4743310.760080834, 3753662.058580503, + 13.8, -2001134.4918292952, -4743310.795543319, 3753662.942459633, + 13.81, -2001133.0215073884, -4743310.83028924, 3753663.8241455415, + 13.82, -2001131.5487193887, -4743310.864296738, 3753664.7036260534, + 13.83, -2001130.0734544063, -4743310.89754395, 3753665.580888996, + 13.84, -2001128.5957015522, -4743310.930009019, 3753666.4559221934, + 13.85, -2001127.1153956298, -4743310.961701492, 3753667.328879416, + 13.86, -2001125.6319948758, -4743310.992924557, 3753668.201399741, + 13.870000000000001, -2001124.145378771, -4743311.023772612, 3753669.0738912895, + 13.88, -2001122.6555678023, -4743311.054262057, 3753669.9463370605, + 13.89, -2001121.1625824564, -4743311.084409297, 3753670.8187200553, + 13.9, -2001119.6664432203, -4743311.114230735, 3753671.6910232743, + 13.91, -2001118.1671705807, -4743311.1437427765, 3753672.563229717, + 13.92, -2001116.664785025, -4743311.172961823, 3753673.4353223844, + 13.93, -2001115.1593070398, -4743311.201904279, 3753674.307284277, + 13.94, -2001113.650757112, -4743311.230586549, 3753675.179098395, + 13.950000000000001, -2001112.1391557285, -4743311.259025038, 3753676.0507477378, + 13.96, -2001110.624523377, -4743311.287236147, 3753676.9222153067, + 13.97, -2001109.106880543, -4743311.315236281, 3753677.7934841015, + 13.98, -2001107.5862477145, -4743311.343041844, 3753678.6645371234, + 13.99, -2001106.0626453778, -4743311.37066924, 3753679.535357373, + 14, -2001104.5360940201, -4743311.39813487, 3753680.4059278485, + 14.01, -2001103.0066141284, -4743311.425455144, 3753681.276231552, + 14.02, -2001101.4742261893, -4743311.4526464585, 3753682.1462514834, + 14.030000000000001, -2001099.9389506904, -4743311.479725222, 3753683.015970643, + 14.040000000000001, -2001098.4008081178, -4743311.506707837, 3753683.8853720315, + 14.05, -2001096.8598189591, -4743311.533610708, 3753684.7544386485, + 14.06, -2001095.3160037005, -4743311.560450235, 3753685.6231534947, + 14.07, -2001093.7693828295, -4743311.587242827, 3753686.491499571, + 14.08, -2001092.2199768329, -4743311.614004885, 3753687.359459877, + 14.09, -2001090.6678061972, -4743311.640752813, 3753688.2270174124, + 14.1, -2001089.1128914093, -4743311.667503013, 3753689.094155179, + 14.11, -2001087.5552529572, -4743311.694271893, 3753689.960856177, + 14.120000000000001, -2001085.9949113268, -4743311.721075854, 3753690.8271034057, + 14.13, -2001084.4318870055, -4743311.747931302, 3753691.6928798654, + 14.14, -2001082.8662004797, -4743311.774854636, 3753692.558168558, + 14.15, -2001081.2978722365, -4743311.801862265, 3753693.4229524815, + 14.16, -2001079.7269227633, -4743311.828970588, 3753694.287214639, + 14.17, -2001078.1533725462, -4743311.856196014, 3753695.1509380294, + 14.18, -2001076.577242073, -4743311.883554943, 3753696.014105652, + 14.19, -2001074.99855183, -4743311.911063781, 3753696.876700509, + 14.200000000000001, -2001073.4173223043, -4743311.938738929, 3753697.7387055987, + 14.21, -2001071.833573983, -4743311.966596792, 3753698.6001039236, + 14.22, -2001070.2473273526, -4743311.994653775, 3753699.4608784826, + 14.23, -2001068.6586029006, -4743312.022926282, 3753700.321012277, + 14.24, -2001067.0674211131, -4743312.051430714, 3753701.180488307, + 14.25, -2001065.4738024776, -4743312.080183477, 3753702.0392895713, + 14.26, -2001063.8777674811, -4743312.109200975, 3753702.897399072, + 14.27, -2001062.27933661, -4743312.138499611, 3753703.7547998102, + 14.280000000000001, -2001060.678530352, -4743312.168095788, 3753704.6114747836, + 14.290000000000001, -2001059.0753691932, -4743312.198005911, 3753705.4674069937, + 14.3, -2001057.4698736211, -4743312.2282463815, 3753706.3225794416, + 14.31, -2001055.862064122, -4743312.258833607, 3753707.1769751264, + 14.32, -2001054.2519611833, -4743312.289783988, 3753708.03057705, + 14.33, -2001052.6395852922, -4743312.321113931, 3753708.8833682113, + 14.34, -2001051.0249569349, -4743312.352839838, 3753709.7353316117, + 14.35, -2001049.4080965987, -4743312.384978113, 3753710.5864502504, + 14.36, -2001047.7890247705, -4743312.41754516, 3753711.436707129, + 14.370000000000001, -2001046.167761937, -4743312.450557381, 3753712.286085246, + 14.38, -2001044.5443285855, -4743312.484031184, 3753713.134567603, + 14.39, -2001042.9187452025, -4743312.517982967, 3753713.9821372014, + 14.4, -2001041.2910322754, -4743312.55242914, 3753714.8287770394, + 14.41, -2001039.6612102906, -4743312.587386103, 3753715.6744701187, + 14.42, -2001038.029299735, -4743312.622870259, 3753716.519199439, + 14.43, -2001036.395321096, -4743312.658898015, 3753717.3629480014, + 14.44, -2001034.7592948608, -4743312.695485773, 3753718.205698805, + 14.450000000000001, -2001033.121241515, -4743312.732649935, 3753719.0474348506, + 14.46, -2001031.4811815473, -4743312.770406907, 3753719.8881391394, + 14.47, -2001029.8391354429, -4743312.808773094, 3753720.7277946714, + 14.48, -2001028.1951236897, -4743312.847764897, 3753721.5663844463, + 14.49, -2001026.5491667744, -4743312.887398722, 3753722.4038914647, + 14.5, -2001024.9012851834, -4743312.92769097, 3753723.240298727, + 14.51, -2001023.2514994044, -4743312.968658046, 3753724.0755892326, + 14.52, -2001021.599829924, -4743313.010316355, 3753724.909745984, + 14.530000000000001, -2001019.946297229, -4743313.0526823, 3753725.7427519797, + 14.540000000000001, -2001018.2909218066, -4743313.095772284, 3753726.5745902206, + 14.55, -2001016.6337241437, -4743313.139602712, 3753727.405243707, + 14.56, -2001014.9747247272, -4743313.184189987, 3753728.234695439, + 14.57, -2001013.3139440436, -4743313.229550513, 3753729.0629284186, + 14.58, -2001011.6514025803, -4743313.275700695, 3753729.889925644, + 14.59, -2001009.987120824, -4743313.322656935, 3753730.7156701162, + 14.6, -2001008.3211192612, -4743313.370435635, 3753731.5401448356, + 14.61, -2001006.6534183798, -4743313.4190532025, 3753732.363332802, + 14.620000000000001, -2001004.984038666, -4743313.46852604, 3753733.185217017, + 14.63, -2001003.3130006073, -4743313.518870552, 3753734.00578048, + 14.64, -2001001.64032469, -4743313.570103141, 3753734.825006191, + 14.65, -2000999.9660314007, -4743313.622240211, 3753735.642877152, + 14.66, -2000998.2901412274, -4743313.675298165, 3753736.459376362, + 14.67, -2000996.6126746559, -4743313.729293407, 3753737.274486821, + 14.68, -2000994.933652174, -4743313.784242343, 3753738.0881915297, + 14.69, -2000993.2530942683, -4743313.840161375, 3753738.9004734894, + 14.700000000000001, -2000991.5710214253, -4743313.897066906, 3753739.7113156994, + 14.71, -2000989.8874541332, -4743313.954975341, 3753740.5207011597, + 14.72, -2000988.2024128772, -4743314.013903083, 3753741.328612872, + 14.73, -2000986.515918146, -4743314.073866538, 3753742.1350338357, + 14.74, -2000984.8279904246, -4743314.134882106, 3753742.939947051, + 14.75, -2000983.1386502013, -4743314.196966195, 3753743.7433355185, + 14.76, -2000981.4479179627, -4743314.260135205, 3753744.545182239, + 14.77, -2000979.7558141954, -4743314.324405541, 3753745.3454702115, + 14.780000000000001, -2000978.0623593868, -4743314.389793608, 3753746.144182438, + 14.790000000000001, -2000976.3675740235, -4743314.456315808, 3753746.941301918, + 14.8, -2000974.6714785923, -4743314.523988546, 3753747.7368116514, + 14.81, -2000972.9740935804, -4743314.592828226, 3753748.5306946402, + 14.82, -2000971.2754394745, -4743314.66285125, 3753749.322933882, + 14.83, -2000969.5755367614, -4743314.734074023, 3753750.11351238, + 14.84, -2000967.874405929, -4743314.806512949, 3753750.9024131326, + 14.85, -2000966.1720674625, -4743314.8801844325, 3753751.6896191402, + 14.86, -2000964.4685418503, -4743314.955104874, 3753752.4751134044, + 14.870000000000001, -2000962.7638495786, -4743315.031290681, 3753753.258878925, + 14.88, -2000961.0580111349, -4743315.108758256, 3753754.0408987016, + 14.89, -2000959.3510470053, -4743315.187524002, 3753754.8211557353, + 14.9, -2000957.6429776775, -4743315.267604324, 3753755.5996330264, + 14.91, -2000955.933823637, -4743315.349015622, 3753756.376313574, + 14.92, -2000954.2236053727, -4743315.431774305, 3753757.1511803805, + 14.93, -2000952.5123433706, -4743315.515896775, 3753757.9242164455, + 14.94, -2000950.8000581171, -4743315.601399435, 3753758.6954047685, + 14.950000000000001, -2000949.0867700998, -4743315.688298688, 3753759.4647283503, + 14.96, -2000947.3724998063, -4743315.776610942, 3753760.2321701916, + 14.97, -2000945.6572677218, -4743315.866352594, 3753760.9977132925, + 14.98, -2000943.9410943338, -4743315.957540054, 3753761.761340653, + 14.99, -2000942.2240001298, -4743316.050189722, 3753762.523035274, + 15, -2000940.5060055964, -4743316.144318002, 3753763.2827801555, + 15.01, -2000938.787131221, -4743316.2399413, 3753764.040558298, + 15.02, -2000937.0673974887, -4743316.337076018, 3753764.7963527013, + 15.030000000000001, -2000935.3468248886, -4743316.43573856, 3753765.550146366, + 15.040000000000001, -2000933.625433907, -4743316.535945331, 3753766.301922293, + 15.05, -2000931.9032450304, -4743316.637712732, 3753767.0516634816, + 15.06, -2000930.1802787455, -4743316.74105717, 3753767.799352933, + 15.07, -2000928.4565555402, -4743316.845995046, 3753768.544973648, + 15.08, -2000926.7320959007, -4743316.952542767, 3753769.288508626, + 15.09, -2000925.006920314, -4743317.060716733, 3753770.0299408673, + 15.1, -2000923.2810492662, -4743317.170533351, 3753770.7692533727, + 15.11, -2000921.5545032462, -4743317.282009022, 3753771.5064291423, + 15.120000000000001, -2000919.8273027393, -4743317.395160153, 3753772.2414511754, + 15.13, -2000918.0994682326, -4743317.510003144, 3753772.974302474, + 15.14, -2000916.371020214, -4743317.626554401, 3753773.7049660385, + 15.15, -2000914.6419791693, -4743317.744830327, 3753774.4334248677, + 15.16, -2000912.9123655856, -4743317.864847328, 3753775.1596619636, + 15.17, -2000911.1821999503, -4743317.986621804, 3753775.883660325, + 15.18, -2000909.4515027504, -4743318.110170162, 3753776.605402954, + 15.19, -2000907.7202944718, -4743318.235508803, 3753777.324872848, + 15.200000000000001, -2000905.988595603, -4743318.362654135, 3753778.042053011, + 15.21, -2000904.25642663, -4743318.491622557, 3753778.7569264406, + 15.22, -2000902.5238080393, -4743318.622430474, 3753779.4694761382, + 15.23, -2000900.790760318, -4743318.755094293, 3753780.179685104, + 15.24, -2000899.0573039541, -4743318.889630414, 3753780.8875363385, + 15.25, -2000897.3234594325, -4743319.026055241, 3753781.5930128414, + 15.26, -2000895.589247242, -4743319.16438518, 3753782.2960976143, + 15.27, -2000893.854687869, -4743319.304636634, 3753782.9967736565, + 15.280000000000001, -2000892.1198018, -4743319.446826005, 3753783.6950239684, + 15.290000000000001, -2000890.384609522, -4743319.590969699, 3753784.3908315506, + 15.3, -2000888.6491315227, -4743319.737084119, 3753785.0841794037, + 15.31, -2000886.913388288, -4743319.885185669, 3753785.775050527, + 15.32, -2000885.1774003056, -4743320.035290753, 3753786.463427922, + 15.33, -2000883.4411880611, -4743320.187415772, 3753787.1492945882, + 15.34, -2000881.704772043, -4743320.341577134, 3753787.8326335275, + 15.35, -2000879.9681727372, -4743320.49779124, 3753788.5134277367, + 15.36, -2000878.2314106317, -4743320.656074494, 3753789.1916602203, + 15.370000000000001, -2000876.4945062117, -4743320.816443302, 3753789.8673139764, + 15.38, -2000874.7574799657, -4743320.978914064, 3753790.5403720047, + 15.39, -2000873.0203523797, -4743321.143503186, 3753791.2108173072, + 15.4, -2000871.2831439413, -4743321.310227072, 3753791.878632884, + 15.41, -2000869.5458751372, -4743321.479102126, 3753792.5438017347, + 15.42, -2000867.8085664539, -4743321.65014475, 3753793.2063068603, + 15.43, -2000866.071238378, -4743321.823371351, 3753793.866131261, + 15.44, -2000864.3339113975, -4743321.998798328, 3753794.5232579354, + 15.450000000000001, -2000862.5966059992, -4743322.1764420895, 3753795.1776698865, + 15.46, -2000860.8593426694, -4743322.356319036, 3753795.8293501134, + 15.47, -2000859.122141895, -4743322.538445572, 3753796.4782816162, + 15.48, -2000857.3850241632, -4743322.722838103, 3753797.124447395, + 15.49, -2000855.648009961, -4743322.909513031, 3753797.7678304524, + 15.5, -2000853.911119775, -4743323.09848676, 3753798.4084137855, + 15.51, -2000852.1743740926, -4743323.289775695, 3753799.046180397, + 15.52, -2000850.4377933997, -4743323.483396236, 3753799.681113286, + 15.530000000000001, -2000848.7013981855, -4743323.679364793, 3753800.3131954535, + 15.540000000000001, -2000846.9652089342, -4743323.877697766, 3753800.9424098986, + 15.55, -2000845.2292461342, -4743324.078411556, 3753801.5687396233, + 15.56, -2000843.4935302716, -4743324.281522572, 3753802.1921676267, + 15.57, -2000841.7580818343, -4743324.487047216, 3753802.8126769103, + 15.58, -2000840.0229213086, -4743324.695001889, 3753803.4302504733, + 15.59, -2000838.2880691816, -4743324.905402999, 3753804.044871317, + 15.6, -2000836.55354594, -4743325.118266948, 3753804.656522441, + 15.610000000000001, -2000834.8193720705, -4743325.33361014, 3753805.265186846, + 15.620000000000001, -2000833.085568061, -4743325.551448977, 3753805.8708475325, + 15.63, -2000831.3521543983, -4743325.771799865, 3753806.473487499, + 15.64, -2000829.6191515685, -4743325.994679208, 3753807.0730897496, + 15.65, -2000827.8865800581, -4743326.220103407, 3753807.669637281, + 15.66, -2000826.1544603547, -4743326.448088868, 3753808.263113095, + 15.67, -2000824.4228129461, -4743326.678651994, 3753808.853500192, + 15.68, -2000822.691658318, -4743326.91180919, 3753809.4407815724, + 15.69, -2000820.9610169579, -4743327.147576859, 3753810.0249402365, + 15.700000000000001, -2000819.2309093522, -4743327.385971403, 3753810.6059591835, + 15.71, -2000817.5013559884, -4743327.627009228, 3753811.1838214155, + 15.72, -2000815.772377353, -4743327.870706737, 3753811.758509932, + 15.73, -2000814.0439939331, -4743328.1170803355, 3753812.330007734, + 15.74, -2000812.316226216, -4743328.366146425, 3753812.8982978202, + 15.75, -2000810.589094688, -4743328.617921409, 3753813.4633631925, + 15.76, -2000808.8626198354, -4743328.872421692, 3753814.02518685, + 15.77, -2000807.1368221468, -4743329.129663677, 3753814.5837517944, + 15.780000000000001, -2000805.4117221083, -4743329.389663771, 3753815.1390410247, + 15.790000000000001, -2000803.6873402062, -4743329.652438374, 3753815.6910375427, + 15.8, -2000801.9636969282, -4743329.918003892, 3753816.2397243474, + 15.81, -2000800.2408127608, -4743330.186376726, 3753816.7850844394, + 15.82, -2000798.5187081916, -4743330.457573284, 3753817.3271008204, + 15.83, -2000796.797403707, -4743330.7316099685, 3753817.8657564884, + 15.84, -2000795.0769197932, -4743331.00850318, 3753818.401034445, + 15.85, -2000793.3572769389, -4743331.288269325, 3753818.932917691, + 15.860000000000001, -2000791.6384956287, -4743331.570924807, 3753819.461389225, + 15.870000000000001, -2000789.9205963518, -4743331.85648603, 3753819.9864320504, + 15.88, -2000788.2035995943, -4743332.144969397, 3753820.5080291647, + 15.89, -2000786.4875258424, -4743332.436391314, 3753821.026163569, + 15.9, -2000784.7723955836, -4743332.730768179, 3753821.540818264, + 15.91, -2000783.0582293049, -4743333.028116403, 3753822.0519762505, + 15.92, -2000781.3450474925, -4743333.328452383, 3753822.5596205266, + 15.93, -2000779.6328706346, -4743333.631792529, 3753823.063734096, + 15.94, -2000777.9217192174, -4743333.938153243, 3753823.5642999574, + 15.950000000000001, -2000776.2116137277, -4743334.247550926, 3753824.06130111, + 15.96, -2000774.5025746522, -4743334.560001982, 3753824.5547205545, + 15.97, -2000772.7946224783, -4743334.8755228175, 3753825.0445412924, + 15.98, -2000771.087777693, -4743335.194129838, 3753825.5307463245, + 15.99, -2000769.382060783, -4743335.51583944, 3753826.01331865, + 16, -2000767.6774922344, -4743335.840668033, 3753826.4922412676, + 16.01, -2000765.974092536, -4743336.168632019, 3753826.967497181, + 16.02, -2000764.2718821734, -4743336.499747804, 3753827.4390693894, + 16.03, -2000762.5708816329, -4743336.834031788, 3753827.90694089, + 16.04, -2000760.8711114032, -4743337.171500377, 3753828.3710946892, + 16.05, -2000759.1725919703, -4743337.512169975, 3753828.831513782, + 16.06, -2000757.4753438209, -4743337.856056984, 3753829.2881811713, + 16.07, -2000755.779387442, -4743338.20317781, 3753829.741079856, + 16.080000000000002, -2000754.08474332, -4743338.553548854, 3753830.190192837, + 16.09, -2000752.3914319437, -4743338.907186525, 3753830.6355031165, + 16.1, -2000750.699473798, -4743339.26410722, 3753831.076993692, + 16.11, -2000749.0088893704, -4743339.624327346, 3753831.514647565, + 16.12, -2000747.3196991484, -4743339.987863308, 3753831.9484477364, + 16.13, -2000745.6319236192, -4743340.3547315085, 3753832.3783772066, + 16.14, -2000743.9455832674, -4743340.724948349, 3753832.8044189736, + 16.15, -2000742.260698583, -4743341.098530239, 3753833.2265560413, + 16.16, -2000740.5767601717, -4743341.475411961, 3753833.6449435316, + 16.17, -2000738.89172496, -4743341.855280523, 3753834.060243794, + 16.18, -2000737.2054019573, -4743342.238093842, 3753834.472528759, + 16.19, -2000735.5178070434, -4743342.623841046, 3753834.881803666, + 16.2, -2000733.8289560992, -4743343.012511262, 3753835.2880737577, + 16.21, -2000732.138865005, -4743343.404093615, 3753835.691344274, + 16.22, -2000730.4475496402, -4743343.798577233, 3753836.0916204583, + 16.23, -2000728.755025886, -4743344.195951244, 3753836.4889075523, + 16.240000000000002, -2000727.0613096226, -4743344.596204774, 3753836.8832107964, + 16.25, -2000725.3664167293, -4743344.999326949, 3753837.274535432, + 16.26, -2000723.670363088, -4743345.405306897, 3753837.662886701, + 16.27, -2000721.9731645775, -4743345.814133746, 3753838.0482698446, + 16.28, -2000720.274837079, -4743346.225796622, 3753838.430690105, + 16.29, -2000718.5753964721, -4743346.640284649, 3753838.8101527235, + 16.3, -2000716.874858638, -4743347.057586959, 3753839.1866629412, + 16.31, -2000715.1732394558, -4743347.477692675, 3753839.560226, + 16.32, -2000713.4705548065, -4743347.9005909255, 3753839.930847141, + 16.330000000000002, -2000711.7668205705, -4743348.326270838, 3753840.298531607, + 16.34, -2000710.0620526276, -4743348.754721539, 3753840.6632846375, + 16.35, -2000708.3562668585, -4743349.185932155, 3753841.0251114755, + 16.36, -2000706.649479143, -4743349.619891812, 3753841.3840173623, + 16.37, -2000704.9417053617, -4743350.056589639, 3753841.740007539, + 16.38, -2000703.2329613953, -4743350.49601476, 3753842.0930872457, + 16.39, -2000701.5232631231, -4743350.938156307, 3753842.4432617263, + 16.4, -2000699.8126264259, -4743351.383003401, 3753842.790536222, + 16.41, -2000698.101067184, -4743351.830545172, 3753843.134915974, + 16.42, -2000696.388601278, -4743352.280770747, 3753843.4764062236, + 16.43, -2000694.675244587, -4743352.733669253, 3753843.815012212, + 16.44, -2000692.9610129925, -4743353.189229816, 3753844.150739181, + 16.45, -2000691.2459223745, -4743353.647441563, 3753844.483592372, + 16.46, -2000689.5299886134, -4743354.108293622, 3753844.813577027, + 16.47, -2000687.8132275888, -4743354.571775117, 3753845.140698387, + 16.48, -2000686.0956551814, -4743355.037875179, 3753845.464961694, + 16.490000000000002, -2000684.3772872712, -4743355.5065829335, 3753845.7863721894, + 16.5, -2000682.6581397392, -4743355.977887505, 3753846.104935114, + 16.51, -2000680.9382284647, -4743356.451778023, 3753846.42065571, + 16.52, -2000679.2175693288, -4743356.928243614, 3753846.733539218, + 16.53, -2000677.4961782114, -4743357.407273405, 3753847.0435908814, + 16.54, -2000675.7740709928, -4743357.888856522, 3753847.35081594, + 16.55, -2000674.0512635538, -4743358.372982091, 3753847.6552196355, + 16.56, -2000672.3277717738, -4743358.859639243, 3753847.9568072106, + 16.57, -2000670.6036115335, -4743359.348817102, 3753848.2555839056, + 16.580000000000002, -2000668.8787987127, -4743359.840504794, 3753848.551554963, + 16.59, -2000667.1533491928, -4743360.334691448, 3753848.8447256233, + 16.6, -2000665.4272788528, -4743360.83136619, 3753849.135101128, + 16.61, -2000663.7006035738, -4743361.330518147, 3753849.422686719, + 16.62, -2000661.9733392359, -4743361.832136446, 3753849.7074876386, + 16.63, -2000660.2455017199, -4743362.336210212, 3753849.9895091276, + 16.64, -2000658.5171069046, -4743362.842728575, 3753850.2687564264, + 16.65, -2000656.788170671, -4743363.351680661, 3753850.5452347794, + 16.66, -2000655.0587089001, -4743363.863055596, 3753850.818949424, + 16.67, -2000653.3287374717, -4743364.376842508, 3753851.089905606, + 16.68, -2000651.5982722656, -4743364.893030523, 3753851.3581085643, + 16.69, -2000649.8673291625, -4743365.411608769, 3753851.6235635406, + 16.7, -2000648.135924043, -4743365.932566372, 3753851.8862757776, + 16.71, -2000646.404072787, -4743366.455892459, 3753852.1462505157, + 16.72, -2000644.6717912743, -4743366.981576156, 3753852.4034929965, + 16.73, -2000642.9390953863, -4743367.509606591, 3753852.6580084627, + 16.740000000000002, -2000641.206001002, -4743368.039972894, 3753852.9098021546, + 16.75, -2000639.472524003, -4743368.572664185, 3753853.158879313, + 16.76, -2000637.7386802684, -4743369.107669597, 3753853.405245182, + 16.77, -2000636.0044856793, -4743369.644978255, 3753853.6489050006, + 16.78, -2000634.2699561152, -4743370.184579284, 3753853.8898640117, + 16.79, -2000632.5351074568, -4743370.726461812, 3753854.128127456, + 16.8, -2000630.799955585, -4743371.27061497, 3753854.3637005757, + 16.81, -2000629.064516379, -4743371.817027877, 3753854.596588612, + 16.82, -2000627.32880572, -4743372.365689666, 3753854.8267968064, + 16.830000000000002, -2000625.5928394871, -4743372.916589462, 3753855.0543304, + 16.84, -2000623.8566335617, -4743373.469716394, 3753855.279194636, + 16.85, -2000622.1202038238, -4743374.0250595845, 3753855.5013947543, + 16.86, -2000620.3835661535, -4743374.582608164, 3753855.7209359957, + 16.87, -2000618.6467364312, -4743375.142351258, 3753855.9378236034, + 16.88, -2000616.9097305366, -4743375.704277993, 3753856.1520628184, + 16.89, -2000615.1725643512, -4743376.268377499, 3753856.363658882, + 16.9, -2000613.435253754, -4743376.834638898, 3753856.5726170354, + 16.91, -2000611.6978146262, -4743377.403051322, 3753856.7789425217, + 16.92, -2000609.9602628474, -4743377.973603893, 3753856.98264058, + 16.93, -2000608.222614298, -4743378.546285741, 3753857.1837164536, + 16.94, -2000606.4848848593, -4743379.121085994, 3753857.3821753836, + 16.95, -2000604.7470904097, -4743379.697993775, 3753857.5780226113, + 16.96, -2000603.0092468313, -4743380.276998214, 3753857.771263378, + 16.97, -2000601.2713700032, -4743380.858088437, 3753857.9619029257, + 16.98, -2000599.5334758067, -4743381.441253572, 3753858.1499464964, + 16.990000000000002, -2000597.7955801203, -4743382.026482742, 3753858.3353993306, + 17, -2000596.057698826, -4743382.6137650795, 3753858.51826667, + 17.01, -2000594.3198478038, -4743383.2030897075, 3753858.698553757, + 17.02, -2000592.582042933, -4743383.794445754, 3753858.876265831, + 17.03, -2000590.844300095, -4743384.387822348, 3753859.0514081353, + 17.04, -2000589.1066351694, -4743384.983208612, 3753859.2239859113, + 17.05, -2000587.369064037, -4743385.580593675, 3753859.3940044, + 17.06, -2000585.6316025776, -4743386.179966667, 3753859.5614688434, + 17.07, -2000583.8942666715, -4743386.781316711, 3753859.726384483, + 17.080000000000002, -2000582.157072199, -4743387.384632934, 3753859.888756559, + 17.09, -2000580.4200350412, -4743387.989904464, 3753860.0485903155, + 17.1, -2000578.6831710765, -4743388.597120428, 3753860.205890991, + 17.11, -2000576.9464961872, -4743389.206269955, 3753860.3606638294, + 17.12, -2000575.2100262523, -4743389.817342168, 3753860.5129140713, + 17.13, -2000573.4737771528, -4743390.430326196, 3753860.6626469577, + 17.14, -2000571.7377647688, -4743391.045211166, 3753860.8098677318, + 17.150000000000002, -2000570.00200498, -4743391.661986203, 3753860.9545816323, + 17.16, -2000568.2665136675, -4743392.280640438, 3753861.096793903, + 17.17, -2000566.5313067106, -4743392.901162993, 3753861.236509785, + 17.18, -2000564.7963999908, -4743393.523542998, 3753861.3737345194, + 17.19, -2000563.0618093873, -4743394.14776958, 3753861.5084733483, + 17.2, -2000561.3275507812, -4743394.773831865, 3753861.6407315126, + 17.21, -2000559.5936400522, -4743395.40171898, 3753861.770514254, + 17.22, -2000557.860093081, -4743396.031420051, 3753861.897826814, + 17.23, -2000556.1269257471, -4743396.662924206, 3753862.0226744344, + 17.240000000000002, -2000554.3941539319, -4743397.296220573, 3753862.1450623563, + 17.25, -2000552.6617935153, -4743397.931298276, 3753862.2649958218, + 17.26, -2000550.9298603768, -4743398.568146445, 3753862.3824800714, + 17.27, -2000549.1983703969, -4743399.206754205, 3753862.4975203467, + 17.28, -2000547.4673394565, -4743399.847110684, 3753862.6101218904, + 17.29, -2000545.736783436, -4743400.489205008, 3753862.720289943, + 17.3, -2000544.0067182153, -4743401.133026303, 3753862.828029746, + 17.31, -2000542.2771596746, -4743401.778563699, 3753862.9333465425, + 17.32, -2000540.548123694, -4743402.425806321, 3753863.0362455733, + 17.330000000000002, -2000538.8196261541, -4743403.074743296, 3753863.136732077, + 17.34, -2000537.0916829356, -4743403.725363751, 3753863.2348112995, + 17.35, -2000535.3643099174, -4743404.377656812, 3753863.33048848, + 17.36, -2000533.6375229806, -4743405.031611607, 3753863.423768859, + 17.37, -2000531.9113380061, -4743405.687217264, 3753863.514657681, + 17.38, -2000530.1857708734, -4743406.344462907, 3753863.603160185, + 17.39, -2000528.4608374627, -4743407.003337665, 3753863.6892816126, + 17.400000000000002, -2000526.736553655, -4743407.663830666, 3753863.773027207, + 17.41, -2000525.0129353297, -4743408.325931033, 3753863.8544022087, + 17.42, -2000523.2899983674, -4743408.989627896, 3753863.9334118594, + 17.43, -2000521.567758649, -4743409.654910383, 3753864.0100614, + 17.44, -2000519.8462320534, -4743410.321767617, 3753864.084356073, + 17.45, -2000518.1254344622, -4743410.990188728, 3753864.1563011194, + 17.46, -2000516.4053817552, -4743411.660162843, 3753864.2259017816, + 17.47, -2000514.686089813, -4743412.331679086, 3753864.2931632996, + 17.48, -2000512.9675745151, -4743413.004726586, 3753864.3580909143, + 17.490000000000002, -2000511.249851742, -4743413.679294471, 3753864.42068987, + 17.5, -2000509.5329373744, -4743414.3553718645, 3753864.480965406, + 17.51, -2000507.8168472922, -4743415.032947898, 3753864.538922765, + 17.52, -2000506.1015973764, -4743415.712011695, 3753864.5945671876, + 17.53, -2000504.3872035067, -4743416.392552383, 3753864.647903916, + 17.54, -2000502.6736815628, -4743417.074559091, 3753864.6989381914, + 17.55, -2000500.961047426, -4743417.758020942, 3753864.7476752554, + 17.56, -2000499.249316976, -4743418.442927067, 3753864.7941203495, + 17.57, -2000497.5385060932, -4743419.129266589, 3753864.838278715, + 17.580000000000002, -2000495.8286306579, -4743419.817028639, 3753864.8801555936, + 17.59, -2000494.1197065501, -4743420.50620234, 3753864.9197562267, + 17.6, -2000492.411749651, -4743421.196776822, 3753864.9570858562, + 17.61, -2000490.7047758396, -4743421.888741212, 3753864.9921497237, + 17.62, -2000488.9988009972, -4743422.582084634, 3753865.02495307, + 17.63, -2000487.2938410034, -4743423.276796217, 3753865.055501137, + 17.64, -2000485.5899117386, -4743423.972865087, 3753865.083799166, + 17.650000000000002, -2000483.8870290834, -4743424.670280372, 3753865.1098523987, + 17.66, -2000482.1852089183, -4743425.369031199, 3753865.1336660767, + 17.67, -2000480.484467123, -4743426.069106693, 3753865.155245442, + 17.68, -2000478.7848195776, -4743426.770495983, 3753865.1745957355, + 17.69, -2000477.0862821625, -4743427.4731881935, 3753865.1917221975, + 17.7, -2000475.3888707587, -4743428.177172456, 3753865.2066300716, + 17.71, -2000473.6926012463, -4743428.882437892, 3753865.2193245986, + 17.72, -2000471.9974895043, -4743429.58897363, 3753865.229811019, + 17.73, -2000470.3035514145, -4743430.2967688, 3753865.238094576, + 17.740000000000002, -2000468.6108028563, -4743431.005812527, 3753865.2441805103, + 17.75, -2000466.9192597107, -4743431.716093936, 3753865.248074063, + 17.76, -2000465.228937857, -4743432.427602156, 3753865.2497804766, + 17.77, -2000463.5398531766, -4743433.140326314, 3753865.249304992, + 17.78, -2000461.8520215494, -4743433.854255536, 3753865.246652851, + 17.79, -2000460.1654588548, -4743434.569378949, 3753865.2418292942, + 17.8, -2000458.4801809741, -4743435.28568568, 3753865.2348395633, + 17.81, -2000456.7962037877, -4743436.003164856, 3753865.2256889013, + 17.82, -2000455.1135431742, -4743436.721805604, 3753865.214382549, + 17.830000000000002, -2000453.4322150156, -4743437.441597051, 3753865.200925746, + 17.84, -2000451.7522351923, -4743438.162528325, 3753865.1853237376, + 17.85, -2000450.0736195832, -4743438.884588551, 3753865.1675817617, + 17.86, -2000448.39638407, -4743439.607766857, 3753865.147705062, + 17.87, -2000446.720544531, -4743440.332052369, 3753865.1256988784, + 17.88, -2000445.046116849, -4743441.057434215, 3753865.101568454, + 17.89, -2000443.3731169028, -4743441.783901521, 3753865.075319029, + 17.900000000000002, -2000441.7015605723, -4743442.511443414, 3753865.046955846, + 17.91, -2000440.031463739, -4743443.240049022, 3753865.016484146, + 17.92, -2000438.362842282, -4743443.96970747, 3753864.98390917, + 17.93, -2000436.6957120826, -4743444.700407888, 3753864.949236161, + 17.94, -2000435.0300890205, -4743445.4321394, 3753864.912470359, + 17.95, -2000433.3659889763, -4743446.164891135, 3753864.8736170065, + 17.96, -2000431.7034278298, -4743446.898652217, 3753864.832681344, + 17.97, -2000430.0424214618, -4743447.633411777, 3753864.789668614, + 17.98, -2000428.382985752, -4743448.3691589385, 3753864.744584058, + 17.990000000000002, -2000426.7251365816, -4743449.10588283, 3753864.6974329166, + 18, -2000425.0688898293, -4743449.843572577, 3753864.6482204315, + 18.01, -2000423.4142613767, -4743450.58221731, 3753864.596951845, + 18.02, -2000421.7612671037, -4743451.32180615, 3753864.543632398, + 18.03, -2000420.1099228908, -4743452.062328229, 3753864.488267333, + 18.04, -2000418.4602446181, -4743452.803772671, 3753864.4308618894, + 18.05, -2000416.8122481655, -4743453.5461286055, 3753864.3714213106, + 18.06, -2000415.1659494143, -4743454.289385157, 3753864.3099508374, + 18.07, -2000413.5213642435, -4743455.033531455, 3753864.2464557122, + 18.080000000000002, -2000411.878508534, -4743455.778556624, 3753864.1809411747, + 18.09, -2000410.2373981662, -4743456.524449793, 3753864.1134124678, + 18.1, -2000408.59804902, -4743457.271200085, 3753864.043874833, + 18.11, -2000406.9604769761, -4743458.018796631, 3753863.9723335113, + 18.12, -2000405.3246979145, -4743458.767228558, 3753863.8987937444, + 18.13, -2000403.6907277158, -4743459.516484989, 3753863.823260774, + 18.14, -2000402.0585822598, -4743460.266555055, 3753863.745739841, + 18.150000000000002, -2000400.428277427, -4743461.01742788, 3753863.666236187, + 18.16, -2000398.799829098, -4743461.769092593, 3753863.5847550547, + 18.17, -2000397.173253152, -4743462.52153832, 3753863.5013016844, + 18.18, -2000395.5485654704, -4743463.274754189, 3753863.415881318, + 18.19, -2000393.9257819331, -4743464.028729326, 3753863.3284991966, + 18.2, -2000392.3049184203, -4743464.783452855, 3753863.239160563, + 18.21, -2000390.6859908132, -4743465.53891391, 3753863.1478706575, + 18.22, -2000389.0690149902, -4743466.295101612, 3753863.0546347215, + 18.23, -2000387.4540068328, -4743467.052005087, 3753862.9594579963, + 18.240000000000002, -2000385.8409822213, -4743467.809613469, 3753862.8623457258, + 18.25, -2000384.229957036, -4743468.567915878, 3753862.7633031495, + 18.26, -2000382.6209471563, -4743469.326901443, 3753862.662335509, + 18.27, -2000381.013968463, -4743470.086559292, 3753862.559448044, + 18.28, -2000379.4090368375, -4743470.846878553, 3753862.454646001, + 18.29, -2000377.8061681578, -4743471.607848348, 3753862.3479346163, + 18.3, -2000376.2053783066, -4743472.36945781, 3753862.239319135, + 18.31, -2000374.6066831625, -4743473.131696061, 3753862.1288047964, + 18.32, -2000373.0100986059, -4743473.89455223, 3753862.0163968424, + 18.330000000000002, -2000371.4156405176, -4743474.658015443, 3753861.9021005165, + 18.34, -2000369.8233247781, -4743475.422074829, 3753861.7859210577, + 18.35, -2000368.2331672676, -4743476.186719515, 3753861.66786371, + 18.36, -2000366.6451838652, -4743476.951938625, 3753861.5479337117, + 18.37, -2000365.0593904522, -4743477.717721287, 3753861.4261363056, + 18.38, -2000363.4758029094, -4743478.48405663, 3753861.3024767344, + 18.39, -2000361.8944371159, -4743479.2509337785, 3753861.1769602397, + 18.400000000000002, -2000360.3153089532, -4743480.018341862, 3753861.0495920614, + 18.41, -2000358.7384342998, -4743480.786270003, 3753860.9203774417, + 18.42, -2000357.1638290372, -4743481.554707331, 3753860.7893216214, + 18.43, -2000355.5915090463, -4743482.323642974, 3753860.656429844, + 18.44, -2000354.0214902058, -4743483.093066058, 3753860.5217073495, + 18.45, -2000352.4537883967, -4743483.862965709, 3753860.3851593793, + 18.46, -2000350.8884195, -4743484.633331055, 3753860.2467911756, + 18.47, -2000349.3259461168, -4743485.404341739, 3753860.1060796715, + 18.48, -2000347.7673171097, -4743486.176330707, 3753859.96210507, + 18.490000000000002, -2000346.2125148326, -4743486.949308207, 3753859.814863847, + 18.5, -2000344.6615033622, -4743487.723278251, 3753859.664369979, + 18.51, -2000343.1142467745, -4743488.498244845, 3753859.510637441, + 18.52, -2000341.5707091454, -4743489.274212003, 3753859.3536802097, + 18.53, -2000340.0308545511, -4743490.051183731, 3753859.193512259, + 18.54, -2000338.4946470675, -4743490.829164038, 3753859.0301475655, + 18.55, -2000336.9620507716, -4743491.608156934, 3753858.863600104, + 18.56, -2000335.4330297385, -4743492.38816643, 3753858.6938838526, + 18.57, -2000333.9075480448, -4743493.169196533, 3753858.5210127835, + 18.580000000000002, -2000332.385569766, -4743493.9512512535, 3753858.3450008733, + 18.59, -2000330.8670589793, -4743494.734334601, 3753858.165862099, + 18.6, -2000329.3519797598, -4743495.518450582, 3753857.9836104335, + 18.61, -2000327.8402961842, -4743496.3036032105, 3753857.7982598552, + 18.62, -2000326.3319723287, -4743497.089796494, 3753857.6098243375, + 18.63, -2000324.8269722688, -4743497.877034439, 3753857.4183178577, + 18.64, -2000323.325260081, -4743498.665321059, 3753857.223754389, + 18.650000000000002, -2000321.8267998416, -4743499.45466036, 3753857.02614791, + 18.66, -2000320.3315556264, -4743500.2450563535, 3753856.8255123934, + 18.67, -2000318.8394915112, -4743501.036513046, 3753856.621861817, + 18.68, -2000317.3505715728, -4743501.82903445, 3753856.4152101544, + 18.69, -2000315.864759887, -4743502.622624574, 3753856.2055713823, + 18.7, -2000314.3820205303, -4743503.417287428, 3753855.9929594765, + 18.71, -2000312.9023175782, -4743504.213027018, 3753855.777388412, + 18.72, -2000311.4256151072, -4743505.009847357, 3753855.558872164, + 18.73, -2000309.951877193, -4743505.807752452, 3753855.3374247095, + 18.740000000000002, -2000308.4810679122, -4743506.606746313, 3753855.1130600222, + 18.75, -2000307.0131513407, -4743507.406832949, 3753854.8857920794, + 18.76, -2000305.5480915545, -4743508.208016372, 3753854.6556348545, + 18.77, -2000304.0858526297, -4743509.010300586, 3753854.422602326, + 18.78, -2000302.6263986428, -4743509.813689605, 3753854.1867084666, + 18.79, -2000301.1696936698, -4743510.618187438, 3753853.9479672536, + 18.8, -2000299.7157017868, -4743511.42379809, 3753853.7063926617, + 18.81, -2000298.2643870693, -4743512.230525576, 3753853.461998667, + 18.82, -2000296.8157135942, -4743513.038373901, 3753853.214799245, + 18.830000000000002, -2000295.369645437, -4743513.847347075, 3753852.9648083705, + 18.84, -2000293.9261466742, -4743514.657449109, 3753852.712040019, + 18.85, -2000292.485181382, -4743515.468684012, 3753852.456508168, + 18.86, -2000291.0467136363, -4743516.281055791, 3753852.198226792, + 18.87, -2000289.6107075133, -4743517.094568459, 3753851.9372098655, + 18.88, -2000288.1771270896, -4743517.909226024, 3753851.6734713647, + 18.89, -2000286.7459364403, -4743518.7250324935, 3753851.4070252655, + 18.900000000000002, -2000285.3170996418, -4743519.541991878, 3753851.1378855435, + 18.91, -2000283.8905807703, -4743520.3601081865, 3753850.866066173, + 18.92, -2000282.4663439025, -4743521.179385429, 3753850.591581132, + 18.93, -2000281.0443531142, -4743521.999827616, 3753850.314444394, + 18.94, -2000279.6245724803, -4743522.821438752, 3753850.034669935, + 18.95, -2000278.206966079, -4743523.644222852, 3753849.7522717305, + 18.96, -2000276.7914979854, -4743524.468183922, 3753849.4672637573, + 18.97, -2000275.378132275, -4743525.293325972, 3753849.179659989, + 18.98, -2000273.966833025, -4743526.119653012, 3753848.8894744026, + 18.990000000000002, -2000272.5575643107, -4743526.947169051, 3753848.596720972, + 19, -2000271.1502902084, -4743527.775878096, 3753848.3014136744, + 19.01, -2000269.744974795, -4743528.60578416, 3753848.003566486, + 19.02, -2000268.3415821453, -4743529.436891251, 3753847.7031933805, + 19.03, -2000266.9400763367, -4743530.269203378, 3753847.400308334, + 19.04, -2000265.540421444, -4743531.102724549, 3753847.094925322, + 19.05, -2000264.142581545, -4743531.937458776, 3753846.787058321, + 19.06, -2000262.7465207144, -4743532.773410066, 3753846.4767213054, + 19.07, -2000261.352203028, -4743533.61058243, 3753846.163928251, + 19.080000000000002, -2000259.9595925633, -4743534.448979876, 3753845.848693134, + 19.09, -2000258.5686533952, -4743535.288606414, 3753845.531029929, + 19.1, -2000257.179349601, -4743536.129466053, 3753845.210952612, + 19.11, -2000255.7916452563, -4743536.971562802, 3753844.8884751587, + 19.12, -2000254.4055044365, -4743537.814900672, 3753844.5636115447, + 19.13, -2000253.0208912191, -4743538.659483669, 3753844.236375746, + 19.14, -2000251.6377696786, -4743539.505315806, 3753843.9067817363, + 19.150000000000002, -2000250.2561038919, -4743540.352401091, 3753843.5748434924, + 19.16, -2000248.8758579355, -4743541.200743532, 3753843.240574991, + 19.17, -2000247.496995885, -4743542.05034714, 3753842.903990206, + 19.18, -2000246.119481817, -4743542.901215923, 3753842.565103113, + 19.19, -2000244.7432798073, -4743543.753353891, 3753842.2239276897, + 19.2, -2000243.3683539312, -4743544.606765052, 3753841.8804779085, + 19.21, -2000241.9946682667, -4743545.461453419, 3753841.534767746, + 19.22, -2000240.6221868885, -4743546.317422997, 3753841.18681118, + 19.23, -2000239.2508738728, -4743547.1746777985, 3753840.8366221823, + 19.240000000000002, -2000237.8806932962, -4743548.03322183, 3753840.484214732, + 19.25, -2000236.511609234, -4743548.893059103, 3753840.129602801, + 19.26, -2000235.1435857632, -4743549.754193625, 3753839.772800369, + 19.27, -2000233.7765869596, -4743550.616629408, 3753839.4138214085, + 19.28, -2000232.410576899, -4743551.480370459, 3753839.0526798964, + 19.29, -2000231.0455196586, -4743552.345420788, 3753838.689389807, + 19.3, -2000229.6813793136, -4743553.211784405, 3753838.323965118, + 19.31, -2000228.3181199396, -4743554.079465318, 3753837.9564198027, + 19.32, -2000226.955705614, -4743554.948467535, 3753837.586767838, + 19.330000000000002, -2000225.594100412, -4743555.818795069, 3753837.2150231996, + 19.34, -2000224.23326841, -4743556.690451927, 3753836.8411998614, + 19.35, -2000222.873173684, -4743557.563442119, 3753836.4653118006, + 19.36, -2000221.5137803103, -4743558.437769655, 3753836.0873729927, + 19.37, -2000220.1550523648, -4743559.313438542, 3753835.7073974125, + 19.38, -2000218.796953924, -4743560.190452792, 3753835.3253990356, + 19.39, -2000217.4394490635, -4743561.068816411, 3753834.941391838, + 19.400000000000002, -2000216.0825018599, -4743561.948533413, 3753834.5553897955, + 19.41, -2000214.7260763887, -4743562.829607802, 3753834.167406882, + 19.42, -2000213.3701367266, -4743563.712043592, 3753833.7774570757, + 19.43, -2000212.0146469495, -4743564.595844789, 3753833.38555435, + 19.44, -2000210.6595711338, -4743565.481015405, 3753832.9917126815, + 19.45, -2000209.3048733552, -4743566.367559446, 3753832.5959460447, + 19.46, -2000207.95051769, -4743567.255480924, 3753832.198268417, + 19.47, -2000206.5964682135, -4743568.144783848, 3753831.798693772, + 19.48, -2000205.2426890035, -4743569.035472226, 3753831.397236086, + 19.490000000000002, -2000203.8891441347, -4743569.927550067, 3753830.993909335, + 19.5, -2000202.535797684, -4743570.821021385, 3753830.5887274947, + 19.51, -2000201.182613727, -4743571.715890181, 3753830.18170454, + 19.52, -2000199.82955634, -4743572.612160473, 3753829.772854446, + 19.53, -2000198.476589599, -4743573.509836264, 3753829.3621911886, + 19.54, -2000197.1236775804, -4743574.408921567, 3753828.9497287455, + 19.55, -2000195.7707843601, -4743575.309420389, 3753828.535481088, + 19.56, -2000194.417874015, -4743576.211336741, 3753828.1194621953, + 19.57, -2000193.0649106193, -4743577.1146746315, 3753827.7016860414, + 19.580000000000002, -2000191.711858251, -4743578.019438069, 3753827.2821666026, + 19.59, -2000190.3586809856, -4743578.925631064, 3753826.860917853, + 19.6, -2000189.005342899, -4743579.833257626, 3753826.43795377, + 19.61, -2000187.651808067, -4743580.742321763, 3753826.013288328, + 19.62, -2000186.2980405665, -4743581.652827486, 3753825.586935503, + 19.63, -2000184.9440044735, -4743582.564778803, 3753825.1589092705, + 19.64, -2000183.5896638632, -4743583.478179723, 3753824.7292236052, + 19.650000000000002, -2000182.2349828132, -4743584.393034257, 3753824.2978924853, + 19.66, -2000180.8799253982, -4743585.309346411, 3753823.864929883, + 19.67, -2000179.524455695, -4743586.2271202, 3753823.430349775, + 19.68, -2000178.16853778, -4743587.146359628, 3753822.9941661386, + 19.69, -2000176.8121357283, -4743588.067068706, 3753822.5563929463, + 19.7, -2000175.4552136175, -4743588.989251444, 3753822.117044176, + 19.71, -2000174.097735522, -4743589.912911851, 3753821.6761338026, + 19.72, -2000172.739665519, -4743590.838053936, 3753821.2336758017, + 19.73, -2000171.380967685, -4743591.764681708, 3753820.789684149, + 19.740000000000002, -2000170.0216060951, -4743592.692799177, 3753820.3441728195, + 19.75, -2000168.6615448259, -4743593.622410353, 3753819.8971557897, + 19.76, -2000167.3007479531, -4743594.553519243, 3753819.4486470325, + 19.77, -2000165.9391795534, -4743595.486129859, 3753818.998660528, + 19.78, -2000164.5768037026, -4743596.420246209, 3753818.547210248, + 19.79, -2000163.213584477, -4743597.3558723, 3753818.094310169, + 19.8, -2000161.8494859524, -4743598.293012145, 3753817.639974267, + 19.81, -2000160.4844722054, -4743599.231669752, 3753817.184216518, + 19.82, -2000159.1185073117, -4743600.171849132, 3753816.7270508963, + 19.830000000000002, -2000157.7515553478, -4743601.11355429, 3753816.268491379, + 19.84, -2000156.383580389, -4743602.056789239, 3753815.8085519397, + 19.85, -2000155.0145465117, -4743603.001557986, 3753815.347246555, + 19.86, -2000153.6444177926, -4743603.947864542, 3753814.884589201, + 19.87, -2000152.273158308, -4743604.895712916, 3753814.420593852, + 19.88, -2000150.900732133, -4743605.845107116, 3753813.955274486, + 19.89, -2000149.5271033447, -4743606.796051154, 3753813.4886450754, + 19.900000000000002, -2000148.1522360183, -4743607.748549037, 3753813.0207195976, + 19.91, -2000146.77609423, -4743608.702604773, 3753812.5515120267, + 19.92, -2000145.398642057, -4743609.658222376, 3753812.0810363404, + 19.93, -2000144.0198435744, -4743610.615405853, 3753811.609306514, + 19.94, -2000142.639662858, -4743611.5741592115, 3753811.1363365212, + 19.95, -2000141.258063985, -4743612.534486461, 3753810.6621403378, + 19.96, -2000139.875011031, -4743613.496391613, 3753810.1867319406, + 19.97, -2000138.4904680715, -4743614.459878676, 3753809.7101253048, + 19.98, -2000137.104399184, -4743615.42495166, 3753809.232334406, + 19.990000000000002, -2000135.716768444, -4743616.391614572, 3753808.753373219, + 20, -2000134.327539927, -4743617.359871424, 3753808.2732557207, + 20.01, -2000132.9366777092, -4743618.329726223, 3753807.7919958853, + 20.02, -2000131.5441458682, -4743619.30118298, 3753807.3096076897, + 20.03, -2000130.1499084777, -4743620.274245704, 3753806.8261051085, + 20.04, -2000128.7539296157, -4743621.248918402, 3753806.3415021165, + 20.05, -2000127.3561733582, -4743622.225205086, 3753805.8558126907, + 20.06, -2000125.95660378, -4743623.203109765, 3753805.369050806, + 20.07, -2000124.5551849578, -4743624.182636448, 3753804.881230438, + 20.080000000000002, -2000123.1518809693, -4743625.163789146, 3753804.392365563, + 20.09, -2000121.7466558877, -4743626.1465718625, 3753803.902470155, + 20.1, -2000120.339473792, -4743627.130988614, 3753803.4115581918, + 20.11, -2000118.9302987563, -4743628.117043405, 3753802.919643647, + 20.12, -2000117.5190948576, -4743629.104740247, 3753802.426740497, + 20.13, -2000116.105826172, -4743630.094083148, 3753801.932862716, + 20.14, -2000114.690456775, -4743631.085076119, 3753801.438024282, + 20.150000000000002, -2000113.2729507438, -4743632.077723168, 3753800.942239169, + 20.16, -2000111.8532721533, -4743633.072028304, 3753800.445521352, + 20.17, -2000110.4313850803, -4743634.067995538, 3753799.9478848083, + 20.18, -2000109.0072536003, -4743635.065628877, 3753799.4493435114, + 20.19, -2000107.5808417904, -4743636.064932331, 3753798.949911438, + 20.2, -2000106.1521137264, -4743637.065909913, 3753798.449602565, + 20.21, -2000104.7210334842, -4743638.068565626, 3753797.9484308655, + 20.22, -2000103.2875651398, -4743639.072903484, 3753797.4464103165, + 20.23, -2000101.8516727688, -4743640.078927494, 3753796.943554892, + 20.240000000000002, -2000100.4133204487, -4743641.0866416665, 3753796.439878571, + 20.25, -2000098.9724722551, -4743642.096050011, 3753795.9353953255, + 20.26, -2000097.5290922634, -4743643.107156536, 3753795.430119132, + 20.27, -2000096.0831445502, -4743644.11996525, 3753794.9240639666, + 20.28, -2000094.634593192, -4743645.134480164, 3753794.417243805, + 20.29, -2000093.1834022638, -4743646.150705285, 3753793.9096726216, + 20.3, -2000091.7295358435, -4743647.168644626, 3753793.401364393, + 20.31, -2000090.2729580055, -4743648.188302193, 3753792.892333096, + 20.32, -2000088.8136328268, -4743649.209681998, 3753792.382592703, + 20.330000000000002, -2000087.351524383, -4743650.232788048, 3753791.872157192, + 20.34, -2000085.8865967507, -4743651.257624352, 3753791.361040537, + 20.35, -2000084.4188140056, -4743652.284194922, 3753790.8492567153, + 20.36, -2000082.948140224, -4743653.312503766, 3753790.3368197004, + 20.37, -2000081.474539482, -4743654.342554892, 3753789.82374347, + 20.38, -2000079.9979758565, -4743655.374352311, 3753789.310041998, + 20.39, -2000078.5184134224, -4743656.407900031, 3753788.7957292604, + 20.400000000000002, -2000077.035816256, -4743657.4432020625, 3753788.2808192335, + 20.41, -2000075.550148434, -4743658.480262414, 3753787.7653258913, + 20.42, -2000074.061374032, -4743659.519085095, 3753787.2492632116, + 20.43, -2000072.5694571263, -4743660.559674115, 3753786.7326451675, + 20.44, -2000071.0743617928, -4743661.602033484, 3753786.215485736, + 20.45, -2000069.5760521078, -4743662.646167209, 3753785.6977988924, + 20.46, -2000068.0744921486, -4743663.692079302, 3753785.1795986127, + 20.47, -2000066.5696459892, -4743664.73977377, 3753784.6608988717, + 20.48, -2000065.0614777065, -4743665.789254625, 3753784.1417136444, + 20.490000000000002, -2000063.5499513776, -4743666.840525874, 3753783.6220569084, + 20.5, -2000062.035031077, -4743667.893591527, 3753783.1019426375, + 20.51, -2000060.5166808823, -4743668.948455594, 3753782.581384808, + 20.52, -2000058.9948648687, -4743670.005122082, 3753782.0603973954, + 20.53, -2000057.469547112, -4743671.063595002, 3753781.5389943738, + 20.54, -2000055.9406916893, -4743672.123878365, 3753781.017189721, + 20.55, -2000054.4082626766, -4743673.185976177, 3753780.4949974115, + 20.56, -2000052.8722241493, -4743674.24989245, 3753779.972431421, + 20.57, -2000051.3325401838, -4743675.315631192, 3753779.4495057254, + 20.580000000000002, -2000049.7891748562, -4743676.383196412, 3753778.9262342993, + 20.59, -2000048.2420922434, -4743677.4525921205, 3753778.4026311203, + 20.6, -2000046.6912564202, -4743678.523822326, 3753777.8787101605, + 20.61, -2000045.136631464, -4743679.596891038, 3753777.354485398, + 20.62, -2000043.5781814493, -4743680.671802265, 3753776.8299708087, + 20.63, -2000042.015870454, -4743681.748560018, 3753776.305180366, + 20.64, -2000040.449662553, -4743682.8271683045, 3753775.780128047, + 20.650000000000002, -2000038.8795218233, -4743683.907631136, 3753775.254827828, + 20.66, -2000037.3054123397, -4743684.989952518, 3753774.729293682, + 20.67, -2000035.7272981796, -4743686.074136463, 3753774.203539587, + 20.68, -2000034.1451434186, -4743687.160186981, 3753773.677579517, + 20.69, -2000032.5589121324, -4743688.248108079, 3753773.1514274483, + 20.7, -2000030.9685683982, -4743689.337903766, 3753772.6250973567, + 20.71, -2000029.3740762922, -4743690.429578056, 3753772.0986032183, + 20.72, -2000027.7753998886, -4743691.5231349515, 3753771.571959006, + 20.73, -2000026.1725032646, -4743692.618578466, 3753771.0451786974, + 20.740000000000002, -2000024.565350497, -4743693.715912607, 3753770.518276268, + 20.75, -2000022.953905661, -4743694.815141385, 3753769.991265693, + 20.76, -2000021.3381328334, -4743695.916268812, 3753769.464160949, + 20.77, -2000019.7180074397, -4743697.019296763, 3753768.9369726786, + 20.78, -2000018.0956883358, -4743698.123818795, 3753768.409069499, + 20.79, -2000016.4727928457, -4743699.229533625, 3753767.8799743624, + 20.8, -2000014.8493070295, -4743700.336446589, 3753767.3496879246, + 20.81, -2000013.2252169454, -4743701.444563032, 3753766.818210838, + 20.82, -2000011.6005086545, -4743702.553888294, 3753766.285543757, + 20.830000000000002, -2000009.9751682165, -4743703.66442772, 3753765.751687338, + 20.84, -2000008.3491816905, -4743704.776186648, 3753765.216642234, + 20.85, -2000006.7225351364, -4743705.889170422, 3753764.6804090985, + 20.86, -2000005.0952146144, -4743707.003384384, 3753764.1429885854, + 20.87, -2000003.4672061838, -4743708.118833875, 3753763.604381351, + 20.88, -2000001.838495905, -4743709.235524237, 3753763.0645880476, + 20.89, -2000000.2090698364, -4743710.353460811, 3753762.5236093304, + 20.900000000000002, -1999998.5789140395, -4743711.47264894, 3753761.981445853, + 20.91, -1999996.948014573, -4743712.593093964, 3753761.43809827, + 20.92, -1999995.3163574967, -4743713.714801228, 3753760.893567236, + 20.93, -1999993.6839288706, -4743714.8377760695, 3753760.347853404, + 20.94, -1999992.0507147545, -4743715.962023834, 3753759.800957429, + 20.95, -1999990.4167012072, -4743717.08754986, 3753759.2528799656, + 20.96, -1999988.7818742904, -4743718.214359493, 3753758.7036216687, + 20.97, -1999987.1462200624, -4743719.342458072, 3753758.1531831897, + 20.98, -1999985.5097245832, -4743720.47185094, 3753757.601565185, + 20.990000000000002, -1999983.8723739127, -4743721.602543439, 3753757.0487683085, + 21, -1999982.2341541105, -4743722.734540908, 3753756.4947932134, + 21.01, -1999980.5950512362, -4743723.867848692, 3753755.939640556, + 21.02, -1999978.9550513502, -4743725.002472132, 3753755.383310989, + 21.03, -1999977.314140512, -4743726.138416571, 3753754.8258051663, + 21.04, -1999975.6723047805, -4743727.275687346, 3753754.267123743, + 21.05, -1999974.029530217, -4743728.414289804, 3753753.7072673733, + 21.06, -1999972.38580288, -4743729.554229286, 3753753.146236711, + 21.07, -1999970.7411088299, -4743730.69551113, 3753752.5840324103, + 21.080000000000002, -1999969.095434126, -4743731.838140681, 3753752.020655126, + 21.09, -1999967.4487648283, -4743732.982123281, 3753751.4561055116, + 21.1, -1999965.8010869962, -4743734.127464271, 3753750.890384222, + 21.11, -1999964.1523866905, -4743735.274168992, 3753750.32349191, + 21.12, -1999962.50264997, -4743736.422242788, 3753749.755429232, + 21.13, -1999960.8518628946, -4743737.571690997, 3753749.1861968404, + 21.14, -1999959.2000115244, -4743738.722518964, 3753748.61579539, + 21.150000000000002, -1999957.5470819187, -4743739.87473203, 3753748.044225536, + 21.16, -1999955.8930601375, -4743741.028335536, 3753747.4714879305, + 21.17, -1999954.2379322406, -4743742.183334825, 3753746.89758323, + 21.18, -1999952.5816842879, -4743743.339735238, 3753746.3225120874, + 21.19, -1999950.9243023384, -4743744.497542116, 3753745.7462751563, + 21.2, -1999949.2657724526, -4743745.6567608025, 3753745.1688730926, + 21.21, -1999947.6060806906, -4743746.817396639, 3753744.59030655, + 21.22, -1999945.945213111, -4743747.979454965, 3753744.010576181, + 21.23, -1999944.2831557742, -4743749.142941125, 3753743.4296826427, + 21.240000000000002, -1999942.61989474, -4743750.30786046, 3753742.847626587, + 21.25, -1999940.9554160682, -4743751.474218312, 3753742.264408669, + 21.26, -1999939.289705818, -4743752.642020021, 3753741.680029543, + 21.27, -1999937.62275005, -4743753.811270932, 3753741.0944898636, + 21.28, -1999935.9545348233, -4743754.981976384, 3753740.507790284, + 21.29, -1999934.285046198, -4743756.154141719, 3753739.9199314592, + 21.3, -1999932.614270234, -4743757.327772279, 3753739.3309140424, + 21.31, -1999930.9421929903, -4743758.502873408, 3753738.740738689, + 21.32, -1999929.2688005273, -4743759.679450445, 3753738.149406053, + 21.330000000000002, -1999927.5940789047, -4743760.857508732, 3753737.5569167878, + 21.34, -1999925.918014182, -4743762.037053612, 3753736.963271549, + 21.35, -1999924.2405924194, -4743763.218090427, 3753736.3684709896, + 21.36, -1999922.561799676, -4743764.400624518, 3753735.7725157635, + 21.37, -1999920.8816220122, -4743765.584661228, 3753735.175406527, + 21.38, -1999919.2000454878, -4743766.7702058945, 3753734.577143932, + 21.39, -1999917.5170561618, -4743767.9572638655, 3753733.977728634, + 21.400000000000002, -1999915.8326400942, -4743769.145840478, 3753733.377161287, + 21.41, -1999914.1467833451, -4743770.335941076, 3753732.775442545, + 21.42, -1999912.4594719743, -4743771.527571, 3753732.1725730626, + 21.43, -1999910.7706920411, -4743772.720735594, 3753731.568553493, + 21.44, -1999909.0804296054, -4743773.915440197, 3753730.9633844914, + 21.45, -1999907.3886707276, -4743775.111690153, 3753730.357066712, + 21.46, -1999905.6954014667, -4743776.309490802, 3753729.7496008086, + 21.47, -1999904.0006078826, -4743777.508847486, 3753729.1409874363, + 21.48, -1999902.3042760352, -4743778.70976555, 3753728.5312272473, + 21.490000000000002, -1999900.6063919843, -4743779.912250331, 3753727.920320898, + 21.5, -1999898.9069417892, -4743781.116307175, 3753727.308269042, + 21.51, -1999897.20591151, -4743782.3219414195, 3753726.6950723324, + 21.52, -1999895.5032872066, -4743783.529158409, 3753726.080731425, + 21.53, -1999893.7990549384, -4743784.737963484, 3753725.465246973, + 21.54, -1999892.0932007656, -4743785.94836199, 3753724.848619631, + 21.55, -1999890.385710748, -4743787.160359263, 3753724.230850053, + 21.56, -1999888.6765709447, -4743788.373960649, 3753723.611938894, + 21.57, -1999886.9657674157, -4743789.589171489, 3753722.991886807, + 21.580000000000002, -1999885.253286221, -4743790.805997123, 3753722.3706944464, + 21.59, -1999883.5391134203, -4743792.024442894, 3753721.748362467, + 21.6, -1999881.8232350731, -4743793.244514144, 3753721.1248915223, + 21.61, -1999880.1056372393, -4743794.466216213, 3753720.500282268, + 21.62, -1999878.3863059788, -4743795.689554446, 3753719.8745353566, + 21.63, -1999876.6652273515, -4743796.914534183, 3753719.247651444, + 21.64, -1999874.942387417, -4743798.141160765, 3753718.619631183, + 21.650000000000002, -1999873.2177722347, -4743799.369439536, 3753717.9904752285, + 21.66, -1999871.4913678644, -4743800.599375835, 3753717.360184234, + 21.67, -1999869.7631603659, -4743801.830975005, 3753716.7287588543, + 21.68, -1999868.0331357995, -4743803.064242389, 3753716.0961997434, + 21.69, -1999866.3012802247, -4743804.299183327, 3753715.462507556, + 21.7, -1999864.5675797008, -4743805.535803162, 3753714.8276829463, + 21.71, -1999862.8320202883, -4743806.7741072355, 3753714.191726567, + 21.72, -1999861.0945880462, -4743808.014100887, 3753713.554639075, + 21.73, -1999859.3552690344, -4743809.2557894625, 3753712.916421122, + 21.740000000000002, -1999857.6140493131, -4743810.499178301, 3753712.2770733633, + 21.75, -1999855.870914942, -4743811.744272743, 3753711.6365964524, + 21.76, -1999854.1258519806, -4743812.9910781365, 3753710.9949910454, + 21.77, -1999852.3788464884, -4743814.239599816, 3753710.3522577956, + 21.78, -1999850.629884526, -4743815.489843125, 3753709.708397355, + 21.79, -1999848.878952152, -4743816.741813408, 3753709.0634103813, + 21.8, -1999847.1260354272, -4743817.995516005, 3753708.4172975263, + 21.81, -1999845.371120411, -4743819.250956259, 3753707.770059446, + 21.82, -1999843.614193163, -4743820.50813951, 3753707.1216967925, + 21.830000000000002, -1999841.8552397427, -4743821.7670711, 3753706.472210222, + 21.84, -1999840.0942462103, -4743823.027756372, 3753705.8216003873, + 21.85, -1999838.3311986257, -4743824.290200667, 3753705.169867943, + 21.86, -1999836.5660830482, -4743825.554409327, 3753704.517013544, + 21.87, -1999834.798885538, -4743826.820387693, 3753703.8630378447, + 21.88, -1999833.029592155, -4743828.088141108, 3753703.2079414977, + 21.89, -1999831.258188958, -4743829.3576749135, 3753702.551725159, + 21.900000000000002, -1999829.484662007, -4743830.62899445, 3753701.894389481, + 21.91, -1999827.7089973628, -4743831.902105061, 3753701.2359351194, + 21.92, -1999825.931181084, -4743833.177012088, 3753700.5763627263, + 21.93, -1999824.1511992305, -4743834.453720872, 3753699.91567296, + 21.94, -1999822.3690378629, -4743835.732236755, 3753699.2538664714, + 21.95, -1999820.5846830402, -4743837.012565078, 3753698.5909439153, + 21.96, -1999818.7981208228, -4743838.294711185, 3753697.9269059463, + 21.97, -1999817.0093372697, -4743839.578680416, 3753697.2617532187, + 21.98, -1999815.2183184405, -4743840.864478112, 3753696.595486386, + 21.990000000000002, -1999813.4250503955, -4743842.152109617, 3753695.928106103, + 22, -1999811.629519195, -4743843.441580272, 3753695.2596130245, + 22.01, -1999809.8317108976, -4743844.732895419, 3753694.590007803, + 22.02, -1999808.0316115639, -4743846.026060399, 3753693.9192910944, + 22.03, -1999806.2292072529, -4743847.321080553, 3753693.2474635527, + 22.04, -1999804.4244840257, -4743848.617961226, 3753692.5745258313, + 22.05, -1999802.6174279405, -4743849.916707755, 3753691.9004785847, + 22.06, -1999800.8080250574, -4743851.217325485, 3753691.225322467, + 22.07, -1999798.9962614372, -4743852.519819759, 3753690.5490581333, + 22.080000000000002, -1999797.182123138, -4743853.824195915, 3753689.871686237, + 22.09, -1999795.365596221, -4743855.130459297, 3753689.193207432, + 22.1, -1999793.5466667456, -4743856.438615247, 3753688.513622373, + 22.11, -1999791.7253207709, -4743857.7486691065, 3753687.832931714, + 22.12, -1999789.9015443576, -4743859.060626217, 3753687.15113611, + 22.13, -1999788.075323565, -4743860.374491921, 3753686.468236215, + 22.14, -1999786.2466444522, -4743861.690271557, 3753685.784232682, + 22.150000000000002, -1999784.4154930806, -4743863.007970473, 3753685.0991261667, + 22.16, -1999782.5818555085, -4743864.3275940055, 3753684.412917323, + 22.17, -1999780.7457177956, -4743865.6491474975, 3753683.725606804, + 22.18, -1999778.9070660025, -4743866.972636292, 3753683.0371952644, + 22.19, -1999777.0658861888, -4743868.298065729, 3753682.3476833594, + 22.2, -1999775.2221644137, -4743869.625441152, 3753681.657071742, + 22.21, -1999773.3758867374, -4743870.954767901, 3753680.965361067, + 22.22, -1999771.5270392203, -4743872.286051322, 3753680.27255199, + 22.23, -1999769.6756079209, -4743873.619296749, 3753679.5786451623, + 22.240000000000002, -1999767.8215788992, -4743874.95450953, 3753678.8836412397, + 22.25, -1999765.9649382154, -4743876.291695007, 3753678.187540877, + 22.26, -1999764.105671929, -4743877.630858519, 3753677.490344727, + 22.27, -1999762.2437661006, -4743878.972005409, 3753676.792053446, + 22.28, -1999760.3792067883, -4743880.315141018, 3753676.092667686, + 22.29, -1999758.5119800533, -4743881.660270688, 3753675.392188102, + 22.3, -1999756.6420719547, -4743883.007399761, 3753674.6906153485, + 22.31, -1999754.769468552, -4743884.3565335795, 3753673.987950079, + 22.32, -1999752.8941559056, -4743885.707677483, 3753673.2841929477, + 22.330000000000002, -1999751.0161200755, -4743887.060836818, 3753672.5793446116, + 22.34, -1999749.13534712, -4743888.41601692, 3753671.873405721, + 22.35, -1999747.2518231005, -4743889.773223136, 3753671.1663769325, + 22.36, -1999745.365534076, -4743891.132460805, 3753670.4582589, + 22.37, -1999743.476466106, -4743892.493735269, 3753669.7490522764, + 22.38, -1999741.5846052505, -4743893.857051869, 3753669.038757717, + 22.39, -1999739.6899375694, -4743895.22241595, 3753668.327375876, + 22.400000000000002, -1999737.7924491223, -4743896.589832851, 3753667.6149074063, + 22.41, -1999735.8921259693, -4743897.959307916, 3753666.901352965, + 22.42, -1999733.9889541694, -4743899.330846484, 3753666.1867132033, + 22.43, -1999732.0829197832, -4743900.704453898, 3753665.470988778, + 22.44, -1999730.1740088698, -4743902.080135498, 3753664.75418034, + 22.45, -1999728.26220749, -4743903.457896631, 3753664.036288548, + 22.46, -1999726.3475017024, -4743904.837742635, 3753663.3173140525, + 22.47, -1999724.4298775669, -4743906.219678851, 3753662.597257509, + 22.48, -1999722.5093211434, -4743907.6037106225, 3753661.8761195717, + 22.490000000000002, -1999720.5858184923, -4743908.989843292, 3753661.1539008957, + 22.5, -1999718.6593556716, -4743910.378082198, 3753660.4306021333, + 22.51, -1999716.7299187437, -4743911.768432686, 3753659.70622394, + 22.52, -1999714.7974937663, -4743913.1609000955, 3753658.9807669697, + 22.53, -1999712.8620667993, -4743914.555489767, 3753658.2542318758, + 22.54, -1999710.9236239034, -4743915.952207047, 3753657.526619315, + 22.55, -1999708.9821511386, -4743917.351057272, 3753656.7979299393, + 22.56, -1999707.037634563, -4743918.752045789, 3753656.068164403, + 22.57, -1999705.0900602378, -4743920.155177935, 3753655.337323362, + 22.580000000000002, -1999703.1394142222, -4743921.560459055, 3753654.6054074685, + 22.59, -1999701.185682576, -4743922.967894489, 3753653.872417378, + 22.6, -1999699.2288513584, -4743924.377489579, 3753653.1383537436, + 22.61, -1999697.2689066299, -4743925.789249667, 3753652.403217221, + 22.62, -1999695.3058344498, -4743927.203180094, 3753651.6670084624, + 22.63, -1999693.3396208794, -4743928.619286205, 3753650.9297281243, + 22.64, -1999691.370251976, -4743930.037573338, 3753650.1913768593, + 22.650000000000002, -1999689.3977138011, -4743931.458046836, 3753649.4519553226, + 22.66, -1999687.4219924137, -4743932.880712041, 3753648.711464167, + 22.67, -1999685.4430738734, -4743934.305574294, 3753647.969904049, + 22.68, -1999683.460944241, -4743935.732638939, 3753647.2272756216, + 22.69, -1999681.475589575, -4743937.161911316, 3753646.4835795374, + 22.7, -1999679.4869959361, -4743938.593396767, 3753645.738816454, + 22.71, -1999677.4951493838, -4743940.027100634, 3753644.9929870227, + 22.72, -1999675.500035977, -4743941.463028259, 3753644.2460918985, + 22.73, -1999673.5016417764, -4743942.901184982, 3753643.4981317357, + 22.740000000000002, -1999671.4999528418, -4743944.341576148, 3753642.74910719, + 22.75, -1999669.4949552328, -4743945.784207094, 3753641.9990189136, + 22.76, -1999667.4866350088, -4743947.229083167, 3753641.247867562, + 22.77, -1999665.4749782293, -4743948.676209707, 3753640.495653787, + 22.78, -1999663.4599709555, -4743950.125592055, 3753639.7423782465, + 22.79, -1999661.4415992452, -4743951.577235552, 3753638.988041592, + 22.8, -1999659.4198491601, -4743953.03114554, 3753638.2326444793, + 22.81, -1999657.3947067584, -4743954.487327362, 3753637.4761875607, + 22.82, -1999655.3661581005, -4743955.945786362, 3753636.7186714928, + 22.830000000000002, -1999653.3341892462, -4743957.406527877, 3753635.960096928, + 22.84, -1999651.2987862553, -4743958.869557252, 3753635.2004645215, + 22.85, -1999649.259935187, -4743960.334879826, 3753634.4397749268, + 22.86, -1999647.2176221025, -4743961.802500945, 3753633.6780288, + 22.87, -1999645.1718330598, -4743963.272425947, 3753632.9152267925, + 22.88, -1999643.1225541192, -4743964.744660173, 3753632.1513695586, + 22.89, -1999641.069771341, -4743966.21920897, 3753631.386457756, + 22.900000000000002, -1999639.0134707852, -4743967.6960776765, 3753630.6204920365, + 22.91, -1999636.9536385098, -4743969.175271632, 3753629.8534730533, + 22.92, -1999634.8902605758, -4743970.656796182, 3753629.085401462, + 22.93, -1999632.8233230435, -4743972.140656666, 3753628.3162779165, + 22.94, -1999630.7528119716, -4743973.626858426, 3753627.5461030705, + 22.95, -1999628.6787134204, -4743975.1154068075, 3753626.774877581, + 22.96, -1999626.6010134502, -4743976.606307147, 3753626.0026020985, + 22.97, -1999624.5196981188, -4743978.099564788, 3753625.2292772783, + 22.98, -1999622.4347534883, -4743979.595185075, 3753624.4549037763, + 22.990000000000002, -1999620.346165617, -4743981.093173347, 3753623.6794822454, + 23, -1999618.253920565, -4743982.593534945, 3753622.9030133393, + 23.01, -1999616.1580043924, -4743984.096275215, 3753622.125497714, + 23.02, -1999614.0584031588, -4743985.601399494, 3753621.3469360215, + 23.03, -1999611.9551029229, -4743987.108913125, 3753620.567328917, + 23.04, -1999609.8480897455, -4743988.61882145, 3753619.7866770537, + 23.05, -1999607.7373496876, -4743990.131129814, 3753619.00498109, + 23.06, -1999605.6228688064, -4743991.645843553, 3753618.222241675, + 23.07, -1999603.5046331626, -4743993.1629680125, 3753617.438459464, + 23.080000000000002, -1999601.3825793418, -4743994.682725542, 3753616.653388917, + 23.09, -1999599.2559600156, -4743996.2083701035, 3753615.8633412733, + 23.1, -1999597.124548982, -4743997.740940886, 3753615.067131906, + 23.11, -1999594.988373824, -4743999.280372861, 3753614.26482786, + 23.12, -1999592.8474621237, -4744000.826600997, 3753613.4564961824, + 23.13, -1999590.7018414652, -4744002.379560261, 3753612.642203919, + 23.14, -1999588.5515394295, -4744003.939185622, 3753611.8220181162, + 23.150000000000002, -1999586.3965836, -4744005.50541205, 3753610.9960058196, + 23.16, -1999584.23700156, -4744007.078174513, 3753610.164234075, + 23.17, -1999582.0728208916, -4744008.657407979, 3753609.3267699294, + 23.18, -1999579.9040691778, -4744010.243047416, 3753608.4836804275, + 23.19, -1999577.7307740015, -4744011.835027795, 3753607.6350326166, + 23.2, -1999575.5529629453, -4744013.433284084, 3753606.780893542, + 23.21, -1999573.3706635924, -4744015.037751251, 3753605.92133025, + 23.22, -1999571.1839035242, -4744016.6483642645, 3753605.0564097855, + 23.23, -1999568.9927103247, -4744018.265058094, 3753604.186199196, + 23.240000000000002, -1999566.7971115767, -4744019.887767708, 3753603.3107655277, + 23.25, -1999564.597134862, -4744021.516428074, 3753602.4301758255, + 23.26, -1999562.392807764, -4744023.150974162, 3753601.5444971365, + 23.27, -1999560.1841578658, -4744024.791340941, 3753600.6537965056, + 23.28, -1999557.9712127494, -4744026.437463377, 3753599.7581409793, + 23.29, -1999555.7539999976, -4744028.089276441, 3753598.8575976044, + 23.3, -1999553.532547194, -4744029.746715101, 3753597.952233426, + 23.31, -1999551.3068819207, -4744031.409714328, 3753597.0421154904, + 23.32, -1999549.0770317605, -4744033.078209087, 3753596.1273108427, + 23.330000000000002, -1999546.8430242958, -4744034.752134348, 3753595.207886531, + 23.34, -1999544.6048871097, -4744036.431425082, 3753594.2839095998, + 23.35, -1999542.3626477856, -4744038.116016254, 3753593.355447096, + 23.36, -1999540.1163339051, -4744039.805842834, 3753592.422566065, + 23.37, -1999537.8659730516, -4744041.50083979, 3753591.485333553, + 23.38, -1999535.6115928083, -4744043.2009420935, 3753590.543816606, + 23.39, -1999533.353220757, -4744044.906084711, 3753589.5980822695, + 23.400000000000002, -1999531.0908844809, -4744046.61620261, 3753588.648197591, + 23.41, -1999528.8246115623, -4744048.331230762, 3753587.694229616, + 23.42, -1999526.5544295849, -4744050.051104135, 3753586.736245389, + 23.43, -1999524.2803661304, -4744051.775757696, 3753585.774311958, + 23.44, -1999522.002448782, -4744053.505126413, 3753584.808496368, + 23.45, -1999519.720705123, -4744055.239145258, 3753583.838865665, + 23.46, -1999517.435162736, -4744056.977749199, 3753582.865486896, + 23.47, -1999515.1458492028, -4744058.720873201, 3753581.8884271057, + 23.48, -1999512.8527921068, -4744060.468452238, 3753580.9077533414, + 23.490000000000002, -1999510.556019031, -4744062.220421274, 3753579.923532649, + 23.5, -1999508.2555575576, -4744063.976715282, 3753578.935832073, + 23.51, -1999505.9514352693, -4744065.7372692255, 3753577.944718661, + 23.52, -1999503.6436797495, -4744067.502018077, 3753576.950259458, + 23.53, -1999501.3323185807, -4744069.270896805, 3753575.952521511, + 23.54, -1999499.0173793456, -4744071.043840378, 3753574.9515718655, + 23.55, -1999496.6988896271, -4744072.820783761, 3753573.9474775675, + 23.56, -1999494.3768770078, -4744074.601661928, 3753572.9403056637, + 23.57, -1999492.05136907, -4744076.386409845, 3753571.9301231992, + 23.580000000000002, -1999489.722393397, -4744078.174962482, 3753570.916997221, + 23.59, -1999487.3899775718, -4744079.967254804, 3753569.900994774, + 23.6, -1999485.0541491765, -4744081.763221785, 3753568.8821829045, + 23.61, -1999482.7149357938, -4744083.562798388, 3753567.86062866, + 23.62, -1999480.3723650074, -4744085.365919588, 3753566.836399084, + 23.63, -1999478.0264643996, -4744087.17252035, 3753565.809561225, + 23.64, -1999475.6772615528, -4744088.982535642, 3753564.7801821274, + 23.650000000000002, -1999473.3247840498, -4744090.795900433, 3753563.748328837, + 23.66, -1999470.9690594734, -4744092.612549694, 3753562.7140684025, + 23.67, -1999468.6101154066, -4744094.432418392, 3753561.6774678673, + 23.68, -1999466.247979432, -4744096.255441495, 3753560.6385942777, + 23.69, -1999463.8826791323, -4744098.081553971, 3753559.5975146806, + 23.7, -1999461.5142420905, -4744099.910690793, 3753558.554296121, + 23.71, -1999459.1426958893, -4744101.742786924, 3753557.509005646, + 23.72, -1999456.7680681108, -4744103.577777337, 3753556.461710301, + 23.73, -1999454.390386339, -4744105.415596999, 3753555.412477133, + 23.740000000000002, -1999452.0096781554, -4744107.25618088, 3753554.3613731875, + 23.75, -1999449.6259711431, -4744109.099463945, 3753553.308465509, + 23.76, -1999447.2392928852, -4744110.945381166, 3753552.253821146, + 23.77, -1999444.8496709648, -4744112.793867512, 3753551.197507143, + 23.78, -1999442.4571329632, -4744114.644857949, 3753550.139590546, + 23.79, -1999440.0617064647, -4744116.498287447, 3753549.0801384016, + 23.8, -1999437.663419052, -4744118.354090976, 3753548.0192177566, + 23.81, -1999435.2622983062, -4744120.212203502, 3753546.9568956555, + 23.82, -1999432.8583718117, -4744122.072559996, 3753545.893239145, + 23.830000000000002, -1999430.4516671507, -4744123.935095425, 3753544.828315271, + 23.84, -1999428.0422119058, -4744125.79974476, 3753543.7621910796, + 23.85, -1999425.6300336597, -4744127.666442967, 3753542.694933617, + 23.86, -1999423.215159996, -4744129.535125017, 3753541.6266099294, + 23.87, -1999420.7976184965, -4744131.405725877, 3753540.5572870616, + 23.88, -1999418.3774367443, -4744133.278180514, 3753539.4870320614, + 23.89, -1999415.9546423217, -4744135.1524239015, 3753538.415911974, + 23.900000000000002, -1999413.5292628126, -4744137.028391005, 3753537.3439938454, + 23.91, -1999411.1013257983, -4744138.906016793, 3753536.2713447213, + 23.92, -1999408.6708588623, -4744140.785236236, 3753535.198031648, + 23.93, -1999406.2378895879, -4744142.6659843, 3753534.124121672, + 23.94, -1999403.802445557, -4744144.548195955, 3753533.0496818395, + 23.95, -1999401.3645543521, -4744146.431806171, 3753531.9747791947, + 23.96, -1999398.9242435577, -4744148.3167499155, 3753530.8994807857, + 23.97, -1999396.4815407544, -4744150.202962157, 3753529.823853658, + 23.98, -1999394.0364735262, -4744152.0903778635, 3753528.747964857, + 23.990000000000002, -1999391.5890694554, -4744153.978932006, 3753527.6718814294, + 24, -1999389.1393561247, -4744155.868559551, 3753526.5956704207, + 24.01, -1999386.687361117, -4744157.759195468, 3753525.5193988774, + 24.02, -1999384.2331120153, -4744159.650774726, 3753524.4431338455, + 24.03, -1999381.7766364026, -4744161.543232292, 3753523.3669423712, + 24.04, -1999379.3179618604, -4744163.4365031365, 3753522.2908914993, + 24.05, -1999376.8571159728, -4744165.330522228, 3753521.2150482773, + 24.060000000000002, -1999374.3941263221, -4744167.225224535, 3753520.1394797512, + 24.07, -1999371.9290204907, -4744169.120545024, 3753519.064252965, + 24.080000000000002, -1999369.4618260614, -4744171.016418668, 3753517.9894349677, + 24.09, -1999366.9925706177, -4744172.912780432, 3753516.9150928035, + 24.1, -1999364.521281741, -4744174.809565286, 3753515.8412935184, + 24.11, -1999362.0479870155, -4744176.7067082, 3753514.7681041593, + 24.12, -1999359.572714023, -4744178.604144138, 3753513.6955917715, + 24.13, -1999357.0954903474, -4744180.501808075, 3753512.6238234015, + 24.14, -1999354.6163435697, -4744182.399634976, 3753511.552866095, + 24.150000000000002, -1999352.1353012738, -4744184.297559809, 3753510.4827868985, + 24.16, -1999349.652391042, -4744186.195517545, 3753509.4136528573, + 24.17, -1999347.1676404576, -4744188.093443152, 3753508.3455310185, + 24.18, -1999344.6810771024, -4744189.991271596, 3753507.278488427, + 24.19, -1999342.192728561, -4744191.888937851, 3753506.21259213, + 24.2, -1999339.7026224146, -4744193.786376882, 3753505.1479091723, + 24.21, -1999337.210786246, -4744195.683523657, 3753504.0845066006, + 24.22, -1999334.7172476382, -4744197.580313146, 3753503.022451461, + 24.23, -1999332.2220341736, -4744199.476680318, 3753501.9618107993, + 24.240000000000002, -1999329.7251734363, -4744201.372560143, 3753500.902651662, + 24.25, -1999327.2266930072, -4744203.267887586, 3753499.8450410934, + 24.26, -1999324.7266204702, -4744205.162597618, 3753498.7890461423, + 24.27, -1999322.2249834079, -4744207.056625207, 3753497.734733852, + 24.28, -1999319.721809403, -4744208.949905323, 3753496.6821712707, + 24.29, -1999317.217126038, -4744210.842372933, 3753495.631425443, + 24.3, -1999314.7109608967, -4744212.733963007, 3753494.582563416, + 24.310000000000002, -1999312.2033415602, -4744214.6246105125, 3753493.5356522356, + 24.32, -1999309.6942956122, -4744216.514250418, 3753492.490758947, + 24.330000000000002, -1999307.1838506348, -4744218.402817694, 3753491.447950597, + 24.34, -1999304.672034212, -4744220.290247307, 3753490.4072942305, + 24.35, -1999302.1588739257, -4744222.176474228, 3753489.368856895, + 24.36, -1999299.6443973582, -4744224.061433423, 3753488.3327056356, + 24.37, -1999297.1286320938, -4744225.945059864, 3753487.2989074993, + 24.38, -1999294.611605714, -4744227.827288516, 3753486.2675295314, + 24.39, -1999292.0933458016, -4744229.70805435, 3753485.2386387773, + 24.400000000000002, -1999289.5738799395, -4744231.587292333, 3753484.2123022843, + 24.41, -1999287.0532357106, -4744233.464937436, 3753483.1885870984, + 24.42, -1999284.5314406974, -4744235.340924626, 3753482.167560264, + 24.43, -1999282.0085224828, -4744237.215188871, 3753481.149288829, + 24.44, -1999279.4845086501, -4744239.087665143, 3753480.133839838, + 24.45, -1999276.9594267819, -4744240.958288407, 3753479.121280339, + 24.46, -1999274.4333044603, -4744242.826993635, 3753478.1116773766, + 24.47, -1999271.906169268, -4744244.693715791, 3753477.1050979956, + 24.48, -1999269.3780487871, -4744246.558389846, 3753476.1016092435, + 24.490000000000002, -1999266.848970603, -4744248.420950772, 3753475.101278167, + 24.5, -1999264.3189622965, -4744250.281333533, 3753474.1041718116, + 24.51, -1999261.7880514502, -4744252.1394731, 3753473.110357223, + 24.52, -1999259.256265647, -4744253.995304441, 3753472.119901447, + 24.53, -1999256.7236324707, -4744255.848762525, 3753471.1328715305, + 24.54, -1999254.190179503, -4744257.69978232, 3753470.149334519, + 24.55, -1999251.6559343273, -4744259.548298795, 3753469.169357458, + 24.560000000000002, -1999249.120924526, -4744261.39424692, 3753468.1930073947, + 24.57, -1999246.5851776814, -4744263.237561661, 3753467.2203513742, + 24.580000000000002, -1999244.0487213766, -4744265.0781779885, 3753466.251456443, + 24.59, -1999241.511583195, -4744266.916030872, 3753465.2863896475, + 24.6, -1999238.9737907182, -4744268.751055276, 3753464.3252180316, + 24.61, -1999236.4353715302, -4744270.583186176, 3753463.3680086443, + 24.62, -1999233.8963532133, -4744272.4123585345, 3753462.4148285305, + 24.63, -1999231.3567633503, -4744274.238507322, 3753461.465744735, + 24.64, -1999228.8166295232, -4744276.06156751, 3753460.5208243066, + 24.650000000000002, -1999226.275979315, -4744277.881474063, 3753459.5801342884, + 24.66, -1999223.7348403088, -4744279.698161951, 3753458.643741727, + 24.67, -1999221.1932400875, -4744281.511566144, 3753457.7117136708, + 24.68, -1999218.6512062335, -4744283.321621611, 3753456.784117162, + 24.69, -1999216.10876633, -4744285.128263317, 3753455.86101925, + 24.7, -1999213.5659479592, -4744286.9314262355, 3753454.9424869795, + 24.71, -1999211.0227787045, -4744288.731045332, 3753454.0285873963, + 24.72, -1999208.479286148, -4744290.527055576, 3753453.119387548, + 24.73, -1999205.9354978728, -4744292.319391936, 3753452.2149544787, + 24.740000000000002, -1999203.3914414614, -4744294.107989382, 3753451.3153552343, + 24.75, -1999200.8471444966, -4744295.89278288, 3753450.420656862, + 24.76, -1999198.3026345617, -4744297.673707401, 3753449.530926408, + 24.77, -1999195.757939238, -4744299.450697913, 3753448.6462309165, + 24.78, -1999193.2130861108, -4744301.223689385, 3753447.766637437, + 24.79, -1999190.66810276, -4744302.992616784, 3753446.8922130116, + 24.8, -1999188.1230167712, -4744304.75741508, 3753446.02302469, + 24.810000000000002, -1999185.5778557241, -4744306.518019242, 3753445.1591395144, + 24.82, -1999183.0326472037, -4744308.274364239, 3753444.3006245336, + 24.830000000000002, -1999180.487418792, -4744310.026385038, 3753443.4475467927, + 24.84, -1999177.9421980716, -4744311.774016609, 3753442.5999733377, + 24.85, -1999175.3970126256, -4744313.51719392, 3753441.757971216, + 24.86, -1999172.8518900366, -4744315.255851939, 3753440.921607471, + 24.87, -1999170.3068578865, -4744316.989925637, 3753440.09094915, + 24.88, -1999167.7619437603, -4744318.71934998, 3753439.2660633, + 24.89, -1999165.2171752385, -4744320.444059939, 3753438.447016966, + 24.900000000000002, -1999162.672579905, -4744322.163990482, 3753437.6338771945, + 24.91, -1999160.1281853423, -4744323.879076576, 3753436.826711031, + 24.92, -1999157.5840191324, -4744325.589253192, 3753436.0255855215, + 24.93, -1999155.0401088593, -4744327.294455298, 3753435.2305677133, + 24.94, -1999152.496482105, -4744328.994617861, 3753434.4417246506, + 24.95, -1999149.9531664527, -4744330.689675852, 3753433.659123381, + 24.96, -1999147.4101894856, -4744332.379564237, 3753432.88283095, + 24.97, -1999144.8675787854, -4744334.064217988, 3753432.112914404, + 24.98, -1999142.3253619343, -4744335.743572071, 3753431.3494407865, + 24.990000000000002, -1999139.7835665166, -4744337.417561457, 3753430.5924771465, + 25, -1999137.2422201142, -4744339.086121111, 3753429.84209053, + 25.01, -1999134.7013503101, -4744340.749186006, 3753429.0983479815, + 25.02, -1999132.1609846875, -4744342.406691108, 3753428.3613165477, + 25.03, -1999129.621150828, -4744344.058571387, 3753427.6310632746, + 25.04, -1999127.081876315, -4744345.70476181, 3753426.9076552074, + 25.05, -1999124.5431887326, -4744347.345197348, 3753426.191159395, + 25.060000000000002, -1999122.0051156613, -4744348.979812967, 3753425.4816428805, + 25.07, -1999119.4676846848, -4744350.608543637, 3753424.7791727106, + 25.080000000000002, -1999116.9309233855, -4744352.231324328, 3753424.0838159313, + 25.09, -1999114.3948593468, -4744353.848090007, 3753423.395639589, + 25.1, -1999111.859520151, -4744355.45877564, 3753422.7147107297, + 25.11, -1999109.324933381, -4744357.063316202, 3753422.041096399, + 25.12, -1999106.7911266198, -4744358.661646658, 3753421.374863644, + 25.13, -1999104.2581274502, -4744360.2537019765, 3753420.7160795103, + 25.14, -1999101.725963454, -4744361.839417127, 3753420.064811043, + 25.150000000000002, -1999099.1946622147, -4744363.418727077, 3753419.421125289, + 25.16, -1999096.6642513154, -4744364.991566798, 3753418.7850892944, + 25.17, -1999094.1347583379, -4744366.557871255, 3753418.1567701045, + 25.18, -1999091.6062108662, -4744368.117575419, 3753417.536234768, + 25.19, -1999089.078636482, -4744369.670614257, 3753416.9235503264, + 25.2, -1999086.552062768, -4744371.216922741, 3753416.318783829, + 25.21, -1999084.0265173076, -4744372.756435836, 3753415.7220023195, + 25.22, -1999081.5020276827, -4744374.28908851, 3753415.133272846, + 25.23, -1999078.9786214777, -4744375.814815738, 3753414.552662456, + 25.240000000000002, -1999076.4563262737, -4744377.3335524835, 3753413.9802381913, + 25.25, -1999073.9351696535, -4744378.845233712, 3753413.4160670997, + 25.26, -1999071.4151792016, -4744380.349794403, 3753412.860216229, + 25.27, -1999068.8963824988, -4744381.847169513, 3753412.312752624, + 25.28, -1999066.3788071286, -4744383.337294018, 3753411.7737433296, + 25.29, -1999063.862480673, -4744384.8201028835, 3753411.243255392, + 25.3, -1999061.347430717, -4744386.295531081, 3753410.7213558597, + 25.310000000000002, -1999058.8336848416, -4744387.763513579, 3753410.2081117774, + 25.32, -1999056.3212706293, -4744389.223985341, 3753409.7035901896, + 25.330000000000002, -1999053.810215663, -4744390.676881342, 3753409.2078581434, + 25.34, -1999051.300547526, -4744392.122136547, 3753408.720982685, + 25.35, -1999048.7922938014, -4744393.559685928, 3753408.2430308624, + 25.36, -1999046.2854820709, -4744394.989464448, 3753407.774069717, + 25.37, -1999043.780139918, -4744396.411407082, 3753407.314166299, + 25.38, -1999041.2762949257, -4744397.825448796, 3753406.863387653, + 25.39, -1999038.7730570685, -4744399.232521531, 3753406.421034405, + 25.400000000000002, -1999036.265726805, -4744400.637723978, 3753405.983190532, + 25.41, -1999033.753683776, -4744402.041762884, 3753405.549296805, + 25.42, -1999031.2369827465, -4744403.444613973, 3753405.119354753, + 25.43, -1999028.7156784823, -4744404.84625297, 3753404.693365905, + 25.44, -1999026.1898257497, -4744406.246655599, 3753404.271331787, + 25.45, -1999023.6594793138, -4744407.645797585, 3753403.8532539313, + 25.46, -1999021.1246939404, -4744409.04365465, 3753403.4391338625, + 25.47, -1999018.585524395, -4744410.440202522, 3753403.028973112, + 25.48, -1999016.0420254434, -4744411.835416922, 3753402.6227732077, + 25.490000000000002, -1999013.4942518515, -4744413.229273576, 3753402.2205356774, + 25.5, -1999010.9422583843, -4744414.621748209, 3753401.8222620497, + 25.51, -1999008.3860998084, -4744416.012816543, 3753401.4279538537, + 25.52, -1999005.8258308882, -4744417.402454301, 3753401.0376126184, + 25.53, -1999003.2615063908, -4744418.790637212, 3753400.6512398715, + 25.54, -1999000.6931810814, -4744420.177340998, 3753400.2688371427, + 25.55, -1998998.1209097253, -4744421.562541383, 3753399.890405958, + 25.560000000000002, -1998995.544747088, -4744422.946214092, 3753399.5159478486, + 25.57, -1998992.964747936, -4744424.328334847, 3753399.145464342, + 25.580000000000002, -1998990.3809670347, -4744425.708879377, 3753398.778956966, + 25.59, -1998987.793459149, -4744427.087823402, 3753398.416427251, + 25.6, -1998985.2022790455, -4744428.465142648, 3753398.057876724, + 25.61, -1998982.6074814894, -4744429.840812838, 3753397.703306914, + 25.62, -1998980.0091212466, -4744431.214809697, 3753397.35271935, + 25.63, -1998977.4072530833, -4744432.587108952, 3753397.0061155595, + 25.64, -1998974.8019317635, -4744433.957686322, 3753396.6634970717, + 25.650000000000002, -1998972.1932120542, -4744435.326517534, 3753396.324865415, + 25.66, -1998969.5811487213, -4744436.693578314, 3753395.990222119, + 25.67, -1998966.9657965293, -4744438.058844385, 3753395.6595687107, + 25.68, -1998964.347210245, -4744439.42229147, 3753395.3329067193, + 25.69, -1998961.725444633, -4744440.783895294, 3753395.010237673, + 25.7, -1998959.1005544602, -4744442.143631583, 3753394.691563101, + 25.71, -1998956.4725944917, -4744443.501476059, 3753394.376884531, + 25.72, -1998953.841619493, -4744444.857404446, 3753394.066203492, + 25.73, -1998951.2076842296, -4744446.211392472, 3753393.7595215137, + 25.740000000000002, -1998948.5708434677, -4744447.563415856, 3753393.4568401235, + 25.75, -1998945.9311519724, -4744448.913450327, 3753393.1581608485, + 25.76, -1998943.28866451, -4744450.261471607, 3753392.8634852204, + 25.77, -1998940.6434358456, -4744451.607455418, 3753392.572814765, + 25.78, -1998937.9955207456, -4744452.95137749, 3753392.2861510124, + 25.79, -1998935.3449739749, -4744454.293213543, 3753392.00349549, + 25.8, -1998932.6918503004, -4744455.632939302, 3753391.7248497284, + 25.810000000000002, -1998930.0362044857, -4744456.970530492, 3753391.450215253, + 25.82, -1998927.378091298, -4744458.305962837, 3753391.179593596, + 25.830000000000002, -1998924.7175655025, -4744459.639212062, 3753390.9129862823, + 25.84, -1998922.054681865, -4744460.970253889, 3753390.650394843, + 25.85, -1998919.3894951514, -4744462.299064046, 3753390.391820806, + 25.86, -1998916.722060127, -4744463.625618254, 3753390.1372656994, + 25.87, -1998914.0524315576, -4744464.949892239, 3753389.8867310523, + 25.88, -1998911.380664209, -4744466.271861722, 3753389.6402183934, + 25.89, -1998908.7068128465, -4744467.591502433, 3753389.39772925, + 25.900000000000002, -1998906.0309322362, -4744468.908790091, 3753389.1592651526, + 25.91, -1998903.3530771434, -4744470.223700426, 3753388.924827628, + 25.92, -1998900.6733023338, -4744471.536209157, 3753388.694418205, + 25.93, -1998897.9916625735, -4744472.846292009, 3753388.4680384127, + 25.94, -1998895.308212628, -4744474.153924709, 3753388.2456897795, + 25.95, -1998892.6230072626, -4744475.459082978, 3753388.027373834, + 25.96, -1998889.9361012438, -4744476.761742543, 3753387.8130921046, + 25.97, -1998887.2475493362, -4744478.061879128, 3753387.6028461205, + 25.98, -1998884.557406306, -4744479.359468456, 3753387.396637409, + 25.990000000000002, -1998881.8657269187, -4744480.654486252, 3753387.1944675, + 26, -1998879.17256594, -4744481.94690824, 3753386.99633792, + 26.01, -1998876.4779781357, -4744483.236710144, 3753386.8022502004, + 26.02, -1998873.7820182722, -4744484.523867691, 3753386.6122058677, + 26.03, -1998871.084741114, -4744485.808356601, 3753386.426206451, + 26.04, -1998868.3862014269, -4744487.090152601, 3753386.2442534785, + 26.05, -1998865.6864539776, -4744488.369231414, 3753386.066348479, + 26.060000000000002, -1998862.9855535305, -4744489.645568765, 3753385.8924929826, + 26.07, -1998860.283554852, -4744490.919140378, 3753385.722688515, + 26.080000000000002, -1998857.5805127076, -4744492.189921978, 3753385.5569366068, + 26.09, -1998854.8764818625, -4744493.457889288, 3753385.3952387855, + 26.1, -1998852.1715170834, -4744494.723018034, 3753385.23759658, + 26.11, -1998849.4656731354, -4744495.985283939, 3753385.084011519, + 26.12, -1998846.7590047838, -4744497.244662728, 3753384.934485131, + 26.13, -1998844.0515667952, -4744498.501130125, 3753384.7890189444, + 26.14, -1998841.3434139346, -4744499.7546618525, 3753384.6476144874, + 26.150000000000002, -1998838.6346009672, -4744501.005233637, 3753384.5102732894, + 26.16, -1998835.9251826596, -4744502.252821204, 3753384.376996879, + 26.17, -1998833.2152137773, -4744503.4974002745, 3753384.2477867836, + 26.18, -1998830.5047490858, -4744504.738946576, 3753384.122644533, + 26.19, -1998827.7938433506, -4744505.977435829, 3753384.001571655, + 26.2, -1998825.0825513375, -4744507.212843761, 3753383.8845696775, + 26.21, -1998822.370927813, -4744508.445146094, 3753383.771640131, + 26.22, -1998819.6590275415, -4744509.674318555, 3753383.662784542, + 26.23, -1998816.9469052888, -4744510.900336866, 3753383.5580044407, + 26.240000000000002, -1998814.2346158207, -4744512.123176753, 3753383.4573013536, + 26.25, -1998811.522213904, -4744513.342813939, 3753383.360676811, + 26.26, -1998808.8097543025, -4744514.559224148, 3753383.2681323416, + 26.27, -1998806.0972917834, -4744515.772383106, 3753383.179669474, + 26.28, -1998803.3848811118, -4744516.982266534, 3753383.0952897347, + 26.29, -1998800.6725770535, -4744518.188850161, 3753383.014994654, + 26.3, -1998797.9604343746, -4744519.392109706, 3753382.9387857597, + 26.310000000000002, -1998795.2485078392, -4744520.592020899, 3753382.866664582, + 26.32, -1998792.5368522145, -4744521.788559459, 3753382.7986326464, + 26.330000000000002, -1998789.8255222659, -4744522.981701115, 3753382.734691485, + 26.34, -1998787.1145727583, -4744524.1714215875, 3753382.6748426235, + 26.35, -1998784.404058458, -4744525.357696603, 3753382.619087592, + 26.36, -1998781.694034131, -4744526.540501884, 3753382.5674279183, + 26.37, -1998778.9845545422, -4744527.719813156, 3753382.519865131, + 26.38, -1998776.2756744584, -4744528.895606143, 3753382.4764007586, + 26.39, -1998773.567448644, -4744530.06785657, 3753382.437036331, + 26.400000000000002, -1998770.8599318648, -4744531.23654016, 3753382.4017733755, + 26.41, -1998768.1531788872, -4744532.40163264, 3753382.3706134204, + 26.42, -1998765.4472444763, -4744533.56310973, 3753382.343557994, + 26.43, -1998762.7421833978, -4744534.720947157, 3753382.320608626, + 26.44, -1998760.0380504183, -4744535.875120645, 3753382.3017668445, + 26.45, -1998757.3349003019, -4744537.025605918, 3753382.287034177, + 26.46, -1998754.6327878162, -4744538.1723787, 3753382.276412155, + 26.47, -1998751.9317677252, -4744539.315414716, 3753382.269902304, + 26.48, -1998749.2318947949, -4744540.45468969, 3753382.2675061533, + 26.490000000000002, -1998746.5332237917, -4744541.590179346, 3753382.269225232, + 26.5, -1998743.8358094802, -4744542.7218594095, 3753382.2750610686, + 26.51, -1998741.139706627, -4744543.849705603, 3753382.2850151923, + 26.52, -1998738.4449699973, -4744544.973693651, 3753382.299089129, + 26.53, -1998735.751654357, -4744546.093799279, 3753382.3172844104, + 26.54, -1998733.0598144713, -4744547.20999821, 3753382.3396025626, + 26.55, -1998730.3695051074, -4744548.32226617, 3753382.3660451164, + 26.560000000000002, -1998727.6807810294, -4744549.430578883, 3753382.3966135993, + 26.57, -1998724.993697003, -4744550.534912071, 3753382.431309538, + 26.580000000000002, -1998722.308307794, -4744551.635241461, 3753382.470134464, + 26.59, -1998719.6246681686, -4744552.7315427745, 3753382.5130899046, + 26.6, -1998716.9428328928, -4744553.8237917395, 3753382.5601773886, + 26.61, -1998714.262856731, -4744554.911964077, 3753382.611398444, + 26.62, -1998711.5847944496, -4744555.996035513, 3753382.6667546, + 26.63, -1998708.908700815, -4744557.07598177, 3753382.7262473847, + 26.64, -1998706.2346305912, -4744558.151778574, 3753382.7898783265, + 26.650000000000002, -1998703.562638545, -4744559.22340165, 3753382.8576489547, + 26.66, -1998700.8927794418, -4744560.29082672, 3753382.929560797, + 26.67, -1998698.2251080472, -4744561.35402951, 3753383.005615382, + 26.68, -1998695.5596791278, -4744562.412985743, 3753383.085814239, + 26.69, -1998692.8965474474, -4744563.467671145, 3753383.170158896, + 26.7, -1998690.235767773, -4744564.518061439, 3753383.258650882, + 26.71, -1998687.5773948706, -4744565.564132349, 3753383.351291725, + 26.72, -1998684.9214835046, -4744566.605859599, 3753383.448082953, + 26.73, -1998682.2680884413, -4744567.643218916, 3753383.5490260962, + 26.740000000000002, -1998679.6172644468, -4744568.676186021, 3753383.654122682, + 26.75, -1998676.9690662862, -4744569.704736642, 3753383.7633742397, + 26.76, -1998674.3235487258, -4744570.728846499, 3753383.876782296, + 26.77, -1998671.6807665299, -4744571.748491318, 3753383.9943483816, + 26.78, -1998669.040774466, -4744572.763646825, 3753384.1160740242, + 26.79, -1998666.403627298, -4744573.774288742, 3753384.2419607528, + 26.8, -1998663.769379794, -4744574.780392795, 3753384.372010095, + 26.810000000000002, -1998661.1380867166, -4744575.7819347065, 3753384.50622358, + 26.82, -1998658.5098028337, -4744576.778890202, 3753384.6446027365, + 26.830000000000002, -1998655.8845829098, -4744577.771235005, 3753384.787149092, + 26.84, -1998653.2624817109, -4744578.758944841, 3753384.9338641763, + 26.85, -1998650.643554003, -4744579.741995433, 3753385.0847495175, + 26.86, -1998648.027854552, -4744580.720362507, 3753385.239806644, + 26.87, -1998645.4154381226, -4744581.694021786, 3753385.3990370845, + 26.88, -1998642.8063594815, -4744582.662948992, 3753385.562442368, + 26.89, -1998640.2006733937, -4744583.627119854, 3753385.7300240216, + 26.900000000000002, -1998637.5984346245, -4744584.586510094, 3753385.9017835753, + 26.91, -1998634.9996979414, -4744585.541095436, 3753386.0777225574, + 26.92, -1998632.4045181077, -4744586.490851605, 3753386.2578424956, + 26.93, -1998629.8129498905, -4744587.435754323, 3753386.442144919, + 26.94, -1998627.2250480545, -4744588.375779319, 3753386.6306313565, + 26.95, -1998624.6408673672, -4744589.310902313, 3753386.823303337, + 26.96, -1998622.0604625929, -4744590.241099031, 3753387.0201623873, + 26.97, -1998619.4838884966, -4744591.166345196, 3753387.2212100374, + 26.98, -1998616.9111998456, -4744592.086616535, 3753387.4264478153, + 26.990000000000002, -1998614.3424514043, -4744593.001888769, 3753387.6358772498, + 27, -1998611.777697939, -4744593.912137625, 3753387.8494998696, + 27.01, -1998609.2169942153, -4744594.817338826, 3753388.0673172027, + 27.02, -1998606.6603949987, -4744595.717468096, 3753388.289330778, + 27.03, -1998604.107955055, -4744596.61250116, 3753388.515542125, + 27.04, -1998601.5597291498, -4744597.502413742, 3753388.7459527696, + 27.05, -1998599.0157720495, -4744598.387181567, 3753388.980564243, + 27.060000000000002, -1998596.476138519, -4744599.266780358, 3753389.2193780732, + 27.07, -1998593.9408833233, -4744600.141185839, 3753389.4623957863, + 27.080000000000002, -1998591.4100612286, -4744601.010373737, 3753389.709618914, + 27.09, -1998588.8837270015, -4744601.874319773, 3753389.961048985, + 27.1, -1998586.3619354074, -4744602.732999674, 3753390.216687525, + 27.11, -1998583.8447412108, -4744603.586389163, 3753390.476536065, + 27.12, -1998581.3321991784, -4744604.434463963, 3753390.7405961314, + 27.13, -1998578.8243640757, -4744605.2771998, 3753391.0088692545, + 27.14, -1998576.3212906679, -4744606.114572398, 3753391.2813569624, + 27.150000000000002, -1998573.823033722, -4744606.946557483, 3753391.5580607844, + 27.16, -1998571.3296480018, -4744607.773130776, 3753391.8389822473, + 27.17, -1998568.8411882739, -4744608.594268003, 3753392.124122881, + 27.18, -1998566.357709304, -4744609.409944887, 3753392.413484213, + 27.19, -1998563.8792658579, -4744610.220137155, 3753392.707067773, + 27.2, -1998561.4059127008, -4744611.024820529, 3753393.0048750886, + 27.21, -1998558.9377045995, -4744611.823970733, 3753393.306907689, + 27.22, -1998556.4746963184, -4744612.617563494, 3753393.6131671024, + 27.23, -1998554.016942623, -4744613.405574533, 3753393.923654858, + 27.240000000000002, -1998551.5644982802, -4744614.187979575, 3753394.238372483, + 27.25, -1998549.117418055, -4744614.964754348, 3753394.557321507, + 27.26, -1998546.6757567127, -4744615.735874571, 3753394.8805034584, + 27.27, -1998544.2395690198, -4744616.501315971, 3753395.207919865, + 27.28, -1998541.8089097417, -4744617.261054273, 3753395.5395722566, + 27.29, -1998539.3838336437, -4744618.015065201, 3753395.8754621614, + 27.3, -1998536.9643954919, -4744618.763324475, 3753396.2155911075, + 27.310000000000002, -1998534.550650052, -4744619.505807825, 3753396.5599606233, + 27.32, -1998532.142652089, -4744620.242490974, 3753396.908572238, + 27.330000000000002, -1998529.7404563692, -4744620.973349645, 3753397.2614274793, + 27.34, -1998527.3441176577, -4744621.69835956, 3753397.618527876, + 27.35, -1998524.9536907212, -4744622.417496449, 3753397.9798749583, + 27.36, -1998522.5692303241, -4744623.1307360325, 3753398.345470252, + 27.37, -1998520.1907912334, -4744623.838054036, 3753398.715315288, + 27.38, -1998517.818428214, -4744624.5394261805, 3753399.089411593, + 27.39, -1998515.452196031, -4744625.234828196, 3753399.467760697, + 27.400000000000002, -1998513.0921494514, -4744625.924235803, 3753399.850364127, + 27.41, -1998510.73834324, -4744626.607624727, 3753400.2372234142, + 27.42, -1998508.3908321627, -4744627.284970692, 3753400.6283400846, + 27.43, -1998506.049670985, -4744627.956249422, 3753401.0237156674, + 27.44, -1998503.714914473, -4744628.6214366425, 3753401.4233516916, + 27.45, -1998501.3866173918, -4744629.280508076, 3753401.8272496853, + 27.46, -1998499.0648345072, -4744629.933439445, 3753402.235411176, + 27.47, -1998496.7496205857, -4744630.58020648, 3753402.647837695, + 27.48, -1998494.4410303915, -4744631.2207849, 3753403.064530769, + 27.490000000000002, -1998492.1391186917, -4744631.855150432, 3753403.4854919277, + 27.5, -1998489.8439402515, -4744632.4832788, 3753403.910722698, + 27.51, -1998487.5555498358, -4744633.105145726, 3753404.3402246097, + 27.52, -1998485.2740022105, -4744633.720726934, 3753404.7739991895, + 27.53, -1998482.9993521424, -4744634.329998154, 3753405.212047969, + 27.54, -1998480.731654396, -4744634.932935104, 3753405.6543724737, + 27.55, -1998478.4709637382, -4744635.529513512, 3753406.1009742343, + 27.560000000000002, -1998476.2173349336, -4744636.1197091015, 3753406.551854779, + 27.57, -1998473.9708227473, -4744636.703497592, 3753407.0070156343, + 27.580000000000002, -1998471.7314819465, -4744637.280854717, 3753407.4664583323, + 27.59, -1998469.499367296, -4744637.851756195, 3753407.9301843992, + 27.6, -1998467.274533562, -4744638.416177749, 3753408.398195363, + 27.61, -1998465.057035509, -4744638.974095105, 3753408.8704927536, + 27.62, -1998462.8469279045, -4744639.525483991, 3753409.3470781003, + 27.63, -1998460.6442655127, -4744640.070320126, 3753409.82795293, + 27.64, -1998458.4491031002, -4744640.608579237, 3753410.3131187716, + 27.650000000000002, -1998456.261495432, -4744641.140237047, 3753410.8025771533, + 27.66, -1998454.081497273, -4744641.665269281, 3753411.296329604, + 27.67, -1998451.9091633912, -4744642.183651663, 3753411.7943776543, + 27.68, -1998449.7445485503, -4744642.695359917, 3753412.296722829, + 27.69, -1998447.5877075163, -4744643.200369769, 3753412.8033666587, + 27.7, -1998445.433017133, -4744643.699055944, 3753413.3168119914, + 27.71, -1998443.2676029825, -4744644.192324118, 3753413.842729245, + 27.72, -1998441.0911479944, -4744644.680216775, 3753414.3812327306, + 27.73, -1998438.9038473605, -4744645.162740763, 3753414.932210633, + 27.740000000000002, -1998436.70589627, -4744645.639902925, 3753415.495551139, + 27.75, -1998434.4974899136, -4744646.111710112, 3753416.071142433, + 27.76, -1998432.2788234823, -4744646.578169169, 3753416.6588727017, + 27.77, -1998430.0500921654, -4744647.039286946, 3753417.25863013, + 27.78, -1998427.811491154, -4744647.495070285, 3753417.870302905, + 27.79, -1998425.5632156383, -4744647.94552604, 3753418.4937792104, + 27.8, -1998423.3054608079, -4744648.390661055, 3753419.128947233, + 27.810000000000002, -1998421.038421854, -4744648.830482176, 3753419.775695159, + 27.82, -1998418.7622939667, -4744649.264996251, 3753420.433911173, + 27.830000000000002, -1998416.4772723361, -4744649.69421013, 3753421.1034834613, + 27.84, -1998414.1835521525, -4744650.118130658, 3753421.784300209, + 27.85, -1998411.8813286065, -4744650.53676468, 3753422.476249602, + 27.86, -1998409.5707968888, -4744650.950119049, 3753423.179219827, + 27.87, -1998407.2521521884, -4744651.358200607, 3753423.8930990687, + 27.88, -1998404.9255896974, -4744651.761016205, 3753424.617775512, + 27.89, -1998402.5913046047, -4744652.158572687, 3753425.3531373446, + 27.900000000000002, -1998400.2494921014, -4744652.550876902, 3753426.0990727507, + 27.91, -1998397.9003473767, -4744652.937935699, 3753426.855469916, + 27.92, -1998395.5440656226, -4744653.319755921, 3753427.622217027, + 27.93, -1998393.1808420287, -4744653.69634442, 3753428.3992022695, + 27.94, -1998390.810871785, -4744654.06770804, 3753429.186313827, + 27.95, -1998388.4343500822, -4744654.433853629, 3753429.9834398883, + 27.96, -1998386.051472111, -4744654.794788035, 3753430.7904686364, + 27.97, -1998383.6624330604, -4744655.150518104, 3753431.607288259, + 27.98, -1998381.2674281218, -4744655.501050685, 3753432.4337869403, + 27.990000000000002, -1998378.8666524854, -4744655.846392625, 3753433.2698528673, + 28, -1998376.460301341, -4744656.186550771, 3753434.1153742247, + 28.01, -1998374.0485698797, -4744656.521531969, 3753434.9702391988, + 28.02, -1998371.6316532919, -4744656.851343067, 3753435.834335975, + 28.03, -1998369.2097467666, -4744657.175990913, 3753436.707552738, + 28.04, -1998366.7830454956, -4744657.495482354, 3753437.5897776755, + 28.05, -1998364.351744669, -4744657.809824237, 3753438.480898971, + 28.060000000000002, -1998361.9160394762, -4744658.11902341, 3753439.380804812, + 28.07, -1998359.4761251085, -4744658.423086719, 3753440.2893833835, + 28.080000000000002, -1998357.0321967555, -4744658.722021012, 3753441.2065228713, + 28.09, -1998354.5844496086, -4744659.015833138, 3753442.1321114614, + 28.1, -1998352.1330788564, -4744659.304529941, 3753443.0660373378, + 28.11, -1998349.6782796907, -4744659.588118269, 3753444.008188688, + 28.12, -1998347.2202473013, -4744659.866604972, 3753444.9584536976, + 28.13, -1998344.7591768792, -4744660.139996895, 3753445.9167205514, + 28.14, -1998342.2952636133, -4744660.408300884, 3753446.8828774355, + 28.150000000000002, -1998339.8287026952, -4744660.671523789, 3753447.856812535, + 28.16, -1998337.3596893148, -4744660.929672456, 3753448.838414036, + 28.17, -1998334.888418662, -4744661.182753732, 3753449.8275701255, + 28.18, -1998332.4150859278, -4744661.430774467, 3753450.8241689876, + 28.19, -1998329.9398863025, -4744661.6737415055, 3753451.8280988075, + 28.2, -1998327.4630149757, -4744661.911661695, 3753452.839247773, + 28.21, -1998324.984667139, -4744662.144541881, 3753453.8575040675, + 28.22, -1998322.505037981, -4744662.372388917, 3753454.8827558784, + 28.23, -1998320.0243226937, -4744662.595209643, 3753455.9148913906, + 28.240000000000002, -1998317.5427164661, -4744662.8130109105, 3753456.953798789, + 28.25, -1998315.0604144896, -4744663.025799566, 3753457.999366261, + 28.26, -1998312.577611954, -4744663.233582457, 3753459.051481991, + 28.27, -1998310.0945040493, -4744663.43636643, 3753460.1100341654, + 28.28, -1998307.6112859666, -4744663.634158333, 3753461.1749109696, + 28.29, -1998305.128152896, -4744663.826965012, 3753462.2460005893, + 28.3, -1998302.6453000277, -4744664.014793315, 3753463.32319121, + 28.310000000000002, -1998300.1629225519, -4744664.19765009, 3753464.4063710175, + 28.32, -1998297.6812156588, -4744664.375542183, 3753465.495428198, + 28.330000000000002, -1998295.200374539, -4744664.548476445, 3753466.5902509363, + 28.34, -1998292.7205943828, -4744664.716459717, 3753467.6907274183, + 28.35, -1998290.2420703806, -4744664.8794988515, 3753468.7967458307, + 28.36, -1998287.7649977226, -4744665.037600693, 3753469.908194358, + 28.37, -1998285.289571599, -4744665.190772091, 3753471.024961186, + 28.38, -1998282.8159872014, -4744665.339019889, 3753472.1469345004, + 28.39, -1998280.344439718, -4744665.482350939, 3753473.274002488, + 28.400000000000002, -1998277.8751243404, -4744665.620772085, 3753474.406053332, + 28.41, -1998275.4082362584, -4744665.754290176, 3753475.542975221, + 28.42, -1998272.9439706628, -4744665.8829120565, 3753476.684656339, + 28.43, -1998270.4825227438, -4744666.00664458, 3753477.8309848728, + 28.44, -1998268.0240876914, -4744666.125494586, 3753478.981849006, + 28.45, -1998265.5688606966, -4744666.239468926, 3753480.137136926, + 28.46, -1998263.1170369496, -4744666.348574448, 3753481.2967368183, + 28.47, -1998260.6688116402, -4744666.452817997, 3753482.460536868, + 28.48, -1998258.2243799588, -4744666.552206421, 3753483.628425262, + 28.490000000000002, -1998255.7839370961, -4744666.646746569, 3753484.800290184, + 28.5, -1998253.3476782418, -4744666.736445286, 3753485.976019822, + 28.51, -1998250.915798587, -4744666.821309421, 3753487.15550236, + 28.52, -1998248.488493322, -4744666.901345819, 3753488.338625984, + 28.53, -1998246.065957636, -4744666.976561329, 3753489.5252788793, + 28.54, -1998243.648386721, -4744667.046962799, 3753490.715349233, + 28.55, -1998241.2359757666, -4744667.112557075, 3753491.9087252296, + 28.560000000000002, -1998238.828919963, -4744667.173351003, 3753493.1052950555, + 28.57, -1998236.4274144997, -4744667.229351433, 3753494.304946895, + 28.580000000000002, -1998234.0316545684, -4744667.280565211, 3753495.507568935, + 28.59, -1998231.6418353587, -4744667.326999184, 3753496.713049362, + 28.6, -1998229.2581520616, -4744667.3686602, 3753497.92127636, + 28.61, -1998226.8807998665, -4744667.405555107, 3753499.132138115, + 28.62, -1998224.5099739644, -4744667.437690751, 3753500.3455228135, + 28.63, -1998222.1458695459, -4744667.4650739785, 3753501.56131864, + 28.64, -1998219.7886818002, -4744667.487711638, 3753502.779413781, + 28.650000000000002, -1998217.4386059183, -4744667.505610576, 3753503.9996964224, + 28.66, -1998215.0958370904, -4744667.518777641, 3753505.2220547493, + 28.67, -1998212.760570507, -4744667.52721968, 3753506.4463769477, + 28.68, -1998210.4330013585, -4744667.530943541, 3753507.672551203, + 28.69, -1998208.1133248352, -4744667.529956069, 3753508.9004657017, + 28.7, -1998205.8017361274, -4744667.524264111, 3753510.1300086286, + 28.71, -1998203.4984304255, -4744667.513874519, 3753511.3610681687, + 28.72, -1998201.2036029191, -4744667.498794134, 3753512.5935325096, + 28.73, -1998198.9174487994, -4744667.479029807, 3753513.8272898355, + 28.740000000000002, -1998196.6401632563, -4744667.454588385, 3753515.062228333, + 28.75, -1998194.3719414803, -4744667.425476717, 3753516.298236187, + 28.76, -1998192.1129786617, -4744667.391701645, 3753517.535201584, + 28.77, -1998189.863469991, -4744667.353270021, 3753518.773012709, + 28.78, -1998187.623610658, -4744667.310188691, 3753520.0115577476, + 28.79, -1998185.3935958538, -4744667.262464501, 3753521.250724886, + 28.8, -1998183.1736207684, -4744667.210104301, 3753522.4904023097, + 28.810000000000002, -1998180.9638805913, -4744667.153114934, 3753523.730478204, + 28.82, -1998178.7645705144, -4744667.091503253, 3753524.9708407554, + 28.830000000000002, -1998176.5758857266, -4744667.025276101, 3753526.211378149, + 28.84, -1998174.3980214186, -4744666.954440324, 3753527.4519785703, + 28.85, -1998172.2311727814, -4744666.879002775, 3753528.692530206, + 28.86, -1998170.0755350047, -4744666.798970298, 3753529.93292124, + 28.87, -1998167.9313032795, -4744666.714349738, 3753531.1730398596, + 28.88, -1998165.7986727953, -4744666.625147946, 3753532.41277425, + 28.89, -1998163.6778387425, -4744666.531371768, 3753533.652012597, + 28.900000000000002, -1998161.5689963119, -4744666.433028051, 3753534.8906430854, + 28.91, -1998159.4723406935, -4744666.330123641, 3753536.1285539023, + 28.92, -1998157.3880670778, -4744666.222665387, 3753537.3656332316, + 28.93, -1998155.3163706553, -4744666.110660139, 3753538.601769261, + 28.94, -1998153.2574466157, -4744665.994114738, 3753539.8368501747, + 28.95, -1998151.21149015, -4744665.873036036, 3753541.07076416, + 28.96, -1998149.1786964484, -4744665.74743088, 3753542.3033993994, + 28.97, -1998147.1592607012, -4744665.617306113, 3753543.5346440827, + 28.98, -1998145.1533780983, -4744665.482668588, 3753544.764386392, + 28.990000000000002, -1998143.1612438303, -4744665.343525148, 3753545.9925145153, + 29, -1998141.1830530872, -4744665.199882641, 3753547.218916638, + 29.01, -1998139.2190010604, -4744665.051747918, 3753548.4434809443, + 29.02, -1998137.2692829391, -4744664.899127822, 3753549.666095622, + 29.03, -1998135.3340939141, -4744664.742029204, 3753550.886648854, + 29.04, -1998133.4136291759, -4744664.5804589065, 3753552.105028829, + 29.05, -1998131.5080839149, -4744664.4144237805, 3753553.321123731, + 29.060000000000002, -1998129.6176533208, -4744664.243930672, 3753554.534821746, + 29.07, -1998127.7425325844, -4744664.068986429, 3753555.746011059, + 29.080000000000002, -1998125.8829168952, -4744663.8895978965, 3753556.9545798567, + 29.09, -1998124.039001445, -4744663.705771926, 3753558.160416324, + 29.1, -1998122.210981423, -4744663.51751536, 3753559.3634086484, + 29.11, -1998120.3990520204, -4744663.32483505, 3753560.5634450135, + 29.12, -1998118.6034084265, -4744663.12773784, 3753561.7604136053, + 29.13, -1998116.8242458324, -4744662.926230579, 3753562.95420261, + 29.14, -1998115.061759428, -4744662.720320114, 3753564.1447002133, + 29.150000000000002, -1998113.3161444038, -4744662.510013292, 3753565.331794601, + 29.16, -1998111.5875959506, -4744662.295316961, 3753566.5153739583, + 29.17, -1998109.8763092575, -4744662.076237966, 3753567.6953264712, + 29.18, -1998108.1824795157, -4744661.852783158, 3753568.871540325, + 29.19, -1998106.5063019157, -4744661.624959381, 3753570.0439037057, + 29.2, -1998104.8479716475, -4744661.392773486, 3753571.2123047994, + 29.21, -1998103.2076839018, -4744661.156232315, 3753572.376631791, + 29.22, -1998101.5856338681, -4744660.915342719, 3753573.536772866, + 29.23, -1998099.9820167376, -4744660.670111546, 3753574.6926162113, + 29.240000000000002, -1998098.3970277, -4744660.42054564, 3753575.8440500116, + 29.25, -1998096.8308619459, -4744660.1666518515, 3753576.9909624527, + 29.26, -1998095.2837146656, -4744659.908437026, 3753578.1332417205, + 29.27, -1998093.7557810494, -4744659.64590801, 3753579.270776001, + 29.28, -1998092.2472562876, -4744659.379071653, 3753580.403453479, + 29.29, -1998090.7583355706, -4744659.107934801, 3753581.531162341, + 29.3, -1998089.2892140893, -4744658.832504301, 3753582.653790772, + 29.310000000000002, -1998087.8400870329, -4744658.552787, 3753583.771226959, + 29.32, -1998086.4111495924, -4744658.268789747, 3753584.883359086, + 29.330000000000002, -1998085.0025969578, -4744657.980519389, 3753585.9900753386, + 29.34, -1998083.6146243203, -4744657.687982771, 3753587.0912639047, + 29.35, -1998082.2474268689, -4744657.391186742, 3753588.1868129675, + 29.36, -1998080.901199795, -4744657.09013815, 3753589.276610715, + 29.37, -1998079.5761382882, -4744656.784843842, 3753590.36054533, + 29.38, -1998078.2724375394, -4744656.475310663, 3753591.438505001, + 29.39, -1998076.9902927387, -4744656.161545463, 3753592.510377912, + 29.400000000000002, -1998075.7298990763, -4744655.843555087, 3753593.57605225, + 29.41, -1998074.4914517428, -4744655.521346386, 3753594.6354161985, + 29.42, -1998073.275145928, -4744655.194926202, 3753595.6883579455, + 29.43, -1998072.081176823, -4744654.864301388, 3753596.7347656754, + 29.44, -1998070.9097396175, -4744654.5294787865, 3753597.7745275744, + 29.45, -1998069.7610295021, -4744654.190465247, 3753598.807531828, + 29.46, -1998068.6352416673, -4744653.847267617, 3753599.8336666212, + 29.47, -1998067.5325713027, -4744653.499892742, 3753600.8528201412, + 29.48, -1998066.4532135995, -4744653.148347471, 3753601.864880573, + 29.490000000000002, -1998065.3973637477, -4744652.792638651, 3753602.8697361015, + 29.5, -1998064.3652169378, -4744652.43277313, 3753603.867274913, + 29.51, -1998063.356968359, -4744652.068757753, 3753604.857385193, + 29.52, -1998062.3728132034, -4744651.70059937, 3753605.8399551283, + 29.53, -1998061.4129466605, -4744651.328304825, 3753606.814872903, + 29.54, -1998060.4775639204, -4744650.951880969, 3753607.7820267035, + 29.55, -1998059.566860174, -4744650.571334648, 3753608.741304715, + 29.560000000000002, -1998058.6810306106, -4744650.186672706, 3753609.692595124, + 29.57, -1998057.8202704214, -4744649.7979019955, 3753610.635786116, + 29.580000000000002, -1998056.9847747965, -4744649.405029359, 3753611.5707658757, + 29.59, -1998056.1747389266, -4744649.008061649, 3753612.4974225904, + 29.6, -1998055.3903580015, -4744648.607005708, 3753613.415644445, + 29.61, -1998054.6318272115, -4744648.201868387, 3753614.325319624, + 29.62, -1998053.8993417474, -4744647.79265653, 3753615.2263363143, + 29.63, -1998053.193096799, -4744647.379376985, 3753616.1185827022, + 29.64, -1998052.5132875573, -4744646.962036601, 3753617.001946973, + 29.650000000000002, -1998051.8601092116, -4744646.540642224, 3753617.876317311, + 29.66, -1998051.2337569534, -4744646.115200702, 3753618.7415819033, + 29.67, -1998050.6344259726, -4744645.685718882, 3753619.5976289352, + 29.68, -1998050.0623114589, -4744645.252203611, 3753620.444346593, + 29.69, -1998049.5176086035, -4744644.814661737, 3753621.281623061, + 29.7, -1998049.0005125962, -4744644.373100107, 3753622.1093465257, + 29.71, -1998048.5112186274, -4744643.927525567, 3753622.927405173, + 29.72, -1998048.049921888, -4744643.477944965, 3753623.7356871883, + 29.73, -1998047.6168175675, -4744643.024365149, 3753624.5340807564, + 29.740000000000002, -1998047.2121008562, -4744642.566792965, 3753625.322474065, + 29.75, -1998046.8359669454, -4744642.105235262, 3753626.100755298, + 29.76, -1998046.4886110246, -4744641.639698887, 3753626.8688126416, + 29.77, -1998046.1702282846, -4744641.170190685, 3753627.6265342827, + 29.78, -1998045.8810139152, -4744640.696717507, 3753628.3738084054, + 29.79, -1998045.6211631072, -4744640.219286199, 3753629.1105231955, + 29.8, -1998045.3908710503, -4744639.737903605, 3753629.8365668384, + 29.810000000000002, -1998045.190332936, -4744639.252576577, 3753630.5518275215, + 29.82, -1998045.0197439534, -4744638.763311959, 3753631.2561934297, + 29.830000000000002, -1998044.8792992935, -4744638.270116598, 3753631.949552747, + 29.84, -1998044.7691941466, -4744637.772997345, 3753632.6317936615, + 29.85, -1998044.689623703, -4744637.271961044, 3753633.302804358, + 29.86, -1998044.6407831525, -4744636.7670145435, 3753633.962473022, + 29.87, -1998044.6228676857, -4744636.258164689, 3753634.6106878384, + 29.88, -1998044.6360724939, -4744635.745418332, 3753635.247336995, + 29.89, -1998044.680592766, -4744635.228782315, 3753635.8723086747, + 29.900000000000002, -1998044.756623693, -4744634.7082634885, 3753636.4854910662, + 29.91, -1998044.8643604652, -4744634.183868698, 3753637.086772353, + 29.92, -1998045.0039982728, -4744633.655604791, 3753637.676040721, + 29.93, -1998045.1757323064, -4744633.123478617, 3753638.2531843563, + 29.94, -1998045.3797577564, -4744632.58749702, 3753638.818091445, + 29.95, -1998045.6162698125, -4744632.04766685, 3753639.3706501727, + 29.96, -1998045.8854636655, -4744631.503994952, 3753639.910748725, + 29.97, -1998046.1875345055, -4744630.956488174, 3753640.4382752865, + 29.98, -1998046.522677523, -4744630.405153365, 3753640.9531180435, + 29.990000000000002, -1998046.8910879083, -4744629.8499973705, 3753641.4551651827, + 30, -1998047.292960852, -4744629.291027037, 3753641.9443048886 + ] + }, + "orientation":{ + "epoch":"2012-03-15T10:00:00Z", + "unitQuaternion":[ + 0, 0.8026163128314372, 0.18330478351001372, -0.49318981173780907, 0.28101640577952014, + 0.1, 0.0013227646593040121, 0.20465333268019117, 0.6193138482890999, 0.7580010692828163, + 0.2, 0.14805073875752459, 0.032300514271327595, 0.7385492073899783, 0.6569495595514115, + 0.30000000000000004, 0.17355355592732385, -0.05104583424031879, 0.7626609480497291, 0.6209845121672356, + 0.4, 0.16947966984115298, -0.11121210780218886, 0.7671945539255669, 0.6085400767539294, + 0.5, 0.15426075361944108, -0.1631131557431578, 0.7639919881654778, 0.6049082247210986, + 0.6000000000000001, 0.13426973328087502, -0.21015756562913537, 0.7563939241318947, 0.6047095731587785, + 0.7000000000000001, 0.11285359128701503, -0.25269046943366874, 0.7461204953873578, 0.6055706399371912, + 0.8, 0.09234638181887836, -0.2900659301208146, 0.7345961182978961, 0.606384733424063, + 0.9, 0.07466093948263473, -0.32130559320797963, 0.7232631985907602, 0.6066949855183423, + 1, 0.061495499357606405, -0.34532774265387983, 0.7136187732766212, 0.6063953332105259, + 1.1, 0.05381222716603362, -0.36170807190591103, 0.7067847673866889, 0.6055797284550427, + 1.2000000000000002, 0.050136142704081524, -0.3726618837060894, 0.7022725191866236, 0.6045021062185464, + 1.3, 0.049784075738039876, -0.3792969766572864, 0.699829793992119, 0.6032359478201702, + 1.4000000000000001, 0.05217817771993951, -0.3824959528237085, 0.6991552259468161, 0.6017941956138878, + 1.5, 0.0567292057096438, -0.38309538179407604, 0.6998733019734289, 0.6001642165711094, + 1.6, 0.0628445879023309, -0.3819092454230839, 0.701569955665762, 0.5983272376709068, + 1.7000000000000002, 0.06993530428579314, -0.3797472939845786, 0.7038176413752424, 0.5962732373793362, + 1.8, 0.07742288857403613, -0.3774284621814729, 0.7061907479288617, 0.5940101680946679, + 1.9000000000000001, 0.08474741203378244, -0.375789926011837, 0.7082714808211619, 0.591566832333417, + 2, 0.09137730696427464, -0.37569276438208987, 0.7096457901438533, 0.5889889532869071, + 2.1, 0.09684612458055361, -0.37799688798901293, 0.7099036534895767, 0.5863241284362263, + 2.2, 0.10140445067024235, -0.3827953304617482, 0.709028492234192, 0.5834924760106645, + 2.3000000000000003, 0.10599160891316653, -0.3897461523799926, 0.7073102670635416, 0.5801516195370753, + 2.4000000000000004, 0.11439457296843608, -0.4018705750214778, 0.7054864139824301, 0.572453353814516, + 2.5, 0.12147992560512556, -0.41253179555842723, 0.7034971208926875, 0.5658197117669307, + 2.6, 0.1277948092491125, -0.42119405100070234, 0.7017825713968118, 0.5601475525379718, + 2.7, 0.13393755450627692, -0.4273874873850118, 0.7007994285593584, 0.5552304278860095, + 2.8000000000000003, 0.14047981312236815, -0.43061454442635516, 0.7009984429398919, 0.5508518124046016, + 2.9000000000000004, 0.1479711034552336, -0.430343879966814, 0.7028059368165552, 0.5467837897128567, + 3, 0.1569413990177578, -0.4260003698654657, 0.706599768177656, 0.5427797433214673, + 3.1, 0.1679012902716786, -0.4169521810102855, 0.7126789347664845, 0.5385617619326318, + 3.2, 0.18262261802701021, -0.4007606528101461, 0.7221918246466992, 0.5333655847142907, + 3.3000000000000003, 0.20173658579392026, -0.37625139051791273, 0.7351504139214007, 0.5265843806995634, + 3.4000000000000004, 0.22369848088073704, -0.3451311432171494, 0.7497856727674533, 0.518328977142829, + 3.5, 0.24700702394937873, -0.30923501625556565, 0.7644548344195202, 0.5088909912483369, + 3.6, 0.27023751432591453, -0.2705711694685063, 0.7777920527283017, 0.49880101324685633, + 3.7, 0.2920671637807144, -0.23131473899791494, 0.7888087635222512, 0.4888465996137369, + 3.8000000000000003, 0.311282982573062, -0.19376131069265956, 0.7969369774878138, 0.4800530316034566, + 3.9000000000000004, 0.32676051337166306, -0.160258272560693, 0.8020086381195531, 0.47363171067673393, + 4, 0.3374042707881186, -0.13313296511892284, 0.8041683734402181, 0.4709004128381023, + 4.1000000000000005, 0.3420454434700293, -0.11463198534523242, 0.803721932300741, 0.47317594832785487, + 4.2, 0.33902901892938636, -0.10732560725409071, 0.8008666504204158, 0.4818227335812437, + 4.3, 0.32695473906413597, -0.11252648999576274, 0.7956345800742264, 0.4973972282219173, + 4.4, 0.30679096440373316, -0.12697365251524007, 0.7881865303302032, 0.5181881792681129, + 4.5, 0.27971315954521303, -0.14706961432116386, 0.778545495265609, 0.5422158137884637, + 4.6000000000000005, 0.2471461943144619, -0.169246838753502, 0.7668894705379993, 0.567586827001624, + 4.7, 0.21549660760536837, -0.20062219332767262, 0.7593870950811222, 0.5802096065037415, + 4.800000000000001, 0.18482488491348364, -0.23002886820061688, 0.7511771947499561, 0.5904737960304821, + 4.9, 0.15736005776602288, -0.2537106678141853, 0.7430602115471189, 0.5989409246935004, + 5, 0.13572289098009896, -0.26880877576124834, 0.7365829421156337, 0.6056126718638078, + 5.1000000000000005, 0.12261173135194393, -0.2725652727754013, 0.7333074292012131, 0.6106838377508101, + 5.2, 0.12079800867730636, -0.2621830795333186, 0.7344327639819201, 0.6142282874436019, + 5.300000000000001, 0.13196401471494046, -0.2360454456349536, 0.7398639143479536, 0.6160109046608144, + 5.4, 0.15424840501258907, -0.19658942278074576, 0.7472504960707735, 0.6157732736355064, + 5.5, 0.18510815517414897, -0.14704778803020596, 0.7538830250593448, 0.6130026944890359, + 5.6000000000000005, 0.22186524210263692, -0.09092692085512054, 0.7574763016724899, 0.6072378132289789, + 5.7, 0.26173566134266985, -0.031990488268471805, 0.7565654210262149, 0.5983977071724843, + 5.800000000000001, 0.30191043446552224, 0.02589262062757947, 0.7507337369417806, 0.5870081072483629, + 5.9, 0.339665942682864, 0.07896661026781632, 0.74064955527192, 0.5743078948784173, + 6, 0.37245614565085816, 0.12378623145346937, 0.7279116247355297, 0.5622259821853988, + 6.1000000000000005, 0.3979295810145343, 0.15731566655501464, 0.7147298583624284, 0.5532314697982857, + 6.2, 0.4138225313862037, 0.176884023182489, 0.7034820639516097, 0.5500690325388938, + 6.300000000000001, 0.4186405998326521, 0.18117297447292016, 0.6958765530777875, 0.5546820930668028, + 6.4, 0.4137099708512149, 0.17274662506883376, 0.6917525950297994, 0.5661104228109151, + 6.5, 0.4007149139995117, 0.15460586470428359, 0.6900925812640191, 0.5824919000138697, + 6.6000000000000005, 0.38138786163808835, 0.12959953658219756, 0.6896520568588154, 0.6017701384941024, + 6.7, 0.35769147928168704, 0.1005312691502165, 0.6893009837683767, 0.621944067700845, + 6.800000000000001, 0.33191166310439535, 0.07020835818379521, 0.6882489746524377, 0.6412634273263045, + 6.9, 0.30666480730840157, 0.04143832565180753, 0.6861571215354098, 0.6583524631167688, + 7, 0.27608292422241193, 0.022253027658890864, 0.6844140215927809, 0.6744334427947734, + 7.1000000000000005, 0.2522000787587729, 0.009598194814733, 0.6825543055302947, 0.6858736144016799, + 7.2, 0.23949206207470905, 0.005611335275419284, 0.6805781936886633, 0.6924055079179446, + 7.300000000000001, 0.24006922499994723, 0.0125926067141514, 0.6785317703271688, 0.694120184204973, + 7.4, 0.25161685764513875, 0.02851449703506895, 0.6761131889573211, 0.6919153388425385, + 7.5, 0.2718030996187378, 0.0513828702697077, 0.6727988500675951, 0.6861665854792222, + 7.6000000000000005, 0.29846313485838855, 0.07935188790899497, 0.6680491126228517, 0.6770032630199074, + 7.7, 0.3294354878175312, 0.11052597264138378, 0.6614729498594526, 0.6646125227095376, + 7.800000000000001, 0.3626206669035055, 0.14299201377844387, 0.6529452115252101, 0.6494013294383845, + 7.9, 0.39604532945888743, 0.17486614780357396, 0.6426730868532727, 0.6320927390823482, + 8, 0.42792132912375824, 0.20434387253960062, 0.631209369353421, 0.6137602543965617, + 8.1, 0.45668415782441985, 0.22973841767076045, 0.619413661185587, 0.5958074821408359, + 8.200000000000001, 0.48099509404503693, 0.2494927484637174, 0.6083658861580388, 0.579903471732814, + 8.3, 0.4998179035488868, 0.26228473616793097, 0.5991811326511696, 0.5677770255480368, + 8.4, 0.5153915062829271, 0.27021557425669385, 0.5913605763414079, 0.5582542498089008, + 8.5, 0.5293763665472611, 0.274771679379433, 0.5843573699565268, 0.5502614387086532, + 8.6, 0.5423825676483213, 0.2762660780517051, 0.5779995438763418, 0.5435206819504519, + 8.700000000000001, 0.555043376088737, 0.2749321129311381, 0.5720484930513677, 0.5376799285233695, + 8.8, 0.5680388033146356, 0.2709015178024342, 0.5662064704929277, 0.5323105469114393, + 8.9, 0.5821229529891739, 0.26416524767037786, 0.5601176781972635, 0.5268944639083986, + 9, 0.5981546159466657, 0.25450483474839575, 0.5533614140249088, 0.5207969757782225, + 9.1, 0.6171274085023837, 0.2413731595333155, 0.5454343611415811, 0.5132193655914711, + 9.200000000000001, 0.640185491016869, 0.223688313014744, 0.5357163643064415, 0.5031242915288392, + 9.3, 0.6635433348964241, 0.2052216350534476, 0.5241325355367032, 0.49282797040386017, + 9.4, 0.6879575542508394, 0.18789632924412697, 0.5111815679296037, 0.47969029343402203, + 9.5, 0.7150149428783158, 0.1721100229822161, 0.4967371724522814, 0.46085133498113273, + 9.600000000000001, 0.7425098869583621, 0.15631419357696186, 0.48178608674007206, 0.4383230626822553, + 9.700000000000001, 0.7684599898964599, 0.139259821492294, 0.46714583912470375, 0.4145488041651352, + 9.8, 0.7912502689379767, 0.12001129690440633, 0.4534205379634592, 0.39233928719136624, + 9.9, 0.8096525473815799, 0.09788633200971208, 0.44094470446437006, 0.37476497452274293, + 10, 0.8227207862084482, 0.07236889074104355, 0.4297258062245779, 0.36500545620319197, + 10.100000000000001, 0.829557977236087, 0.043024913217439564, 0.41939473999545107, 0.36618365789689644, + 10.200000000000001, 0.8289692412441273, 0.009451713006919854, 0.4091624330847702, 0.38119124536710136, + 10.3, 0.8190313206455379, -0.02871228049806171, 0.39777553356724116, 0.41247778806321633, + 10.4, 0.7950794383176973, -0.07297511177002391, 0.38322675948100093, 0.4643926901279334, + 10.5, 0.7457111544586073, -0.127024548722111, 0.36133116103800933, 0.5451783471497005, + 10.600000000000001, 0.6669601846814854, -0.18517400284559987, 0.32872468325453025, 0.6425066406964098, + 10.700000000000001, 0.5587871993126285, -0.24068888525165083, 0.284635919521435, 0.7408158473729174, + 10.8, 0.4261674782473371, -0.2878366999189346, 0.23125278219058773, 0.8258652828356422, + 10.9, 0.2792555026010198, -0.3230179113240686, 0.17327250868702276, 0.8874978484279236, + 11, 0.13170408260073882, -0.3454602886864599, 0.11678801892476501, 0.9217764274500593, + 11.100000000000001, -0.001989657494709099, -0.3571178563556985, 0.0680071664584487, 0.9315781788155287, + 11.200000000000001, -0.10885622847025046, -0.36176249010302386, 0.03235072585712956, 0.9253278623354042, + 11.3, -0.17847984307342335, -0.36351116689520185, 0.014253655617692262, 0.9142217512510338, + 11.4, -0.20234086511224567, -0.36504206130741024, 0.017585151575939928, 0.9085665799632497, + 11.5, -0.17746150671200733, -0.3658283982573714, 0.043987834690173755, 0.9125470218368039, + 11.600000000000001, -0.10343674515391595, -0.36186569712437355, 0.06952650785852305, 0.9238615273414376, + 11.700000000000001, 0.007804559037678183, -0.35302288868798143, 0.09946025581253531, 0.9302803805489641, + 11.8, 0.1426412425253905, -0.3380213372056471, 0.13765318428594062, 0.9200253542052307, + 11.9, 0.28835875587704596, -0.3157889324819249, 0.17915319422583242, 0.8860195884006457, + 12, 0.4312436727052139, -0.28685045094613115, 0.219183264771386, 0.8268642028700216, + 12.100000000000001, 0.5589000253819172, -0.25355081728274853, 0.25379335347070786, 0.747617334214125, + 12.200000000000001, 0.6625314685998834, -0.21973391154205568, 0.280421858906862, 0.6588874276236646, + 12.3, 0.7380824019544231, -0.19006395318362243, 0.298062430272877, 0.5746902202797866, + 12.4, 0.7855753774460935, -0.169269141193261, 0.3068688818232648, 0.5099517364993114, + 12.5, 0.8114190217313466, -0.15839628167367298, 0.3088389006667134, 0.47024283360778063, + 12.600000000000001, 0.8262959099968311, -0.15322935014383765, 0.30728936847846444, 0.4464628533227737, + 12.700000000000001, 0.8332391589396612, -0.15259862329947516, 0.30303878672062, 0.43657033559270353, + 12.8, 0.8342394883377178, -0.15529642204284128, 0.2966287614913599, 0.4381082916983156, + 12.9, 0.8307519196185837, -0.16009477297221508, 0.28850552435179927, 0.4483140351756862, + 13, 0.8240296911447206, -0.16580756983190156, 0.2791238027707388, 0.4642982022635636, + 13.100000000000001, 0.8153706355023084, -0.17133873601017086, 0.269006194462171, 0.4831660497634117, + 13.200000000000001, 0.8062661061820637, -0.1757160250974464, 0.25875952490229237, 0.5020979514181533, + 13.3, 0.7984517779140391, -0.17810601568003942, 0.2490534045031854, 0.5183873139167897, + 13.4, 0.7938649046916416, -0.1778058611626899, 0.24056851204630192, 0.52942457427565, + 13.5, 0.7935671919474836, -0.1746254966129012, 0.23361293268448036, 0.5340243865965669, + 13.600000000000001, 0.7935224247585138, -0.17050985861785178, 0.2268085209004143, 0.5383367388228815, + 13.700000000000001, 0.7931392669611409, -0.1657221926243782, 0.21998001196091763, 0.5432081115132462, + 13.8, 0.7925869633829067, -0.1601939239948456, 0.21320230905013723, 0.5483507888261729, + 13.9, 0.7915761313675379, -0.15619392115666142, 0.2083238683667239, 0.5528217191026713, + 14, 0.7905526138949678, -0.15367673588862418, 0.20441362129005577, 0.5564396615460993, + 14.100000000000001, 0.790013639293712, -0.1513546176913871, 0.200362480815779, 0.5593077021772056, + 14.200000000000001, 0.7901576796556822, -0.14914265160057613, 0.196207729389728, 0.5611682792910029, + 14.3, 0.7911819264533698, -0.14696679063166562, 0.1919830349255833, 0.5617601231893599, + 14.4, 0.7932785903195773, -0.14476378282892327, 0.18771858515623965, 0.5608157077940883, + 14.5, 0.7966391260056421, -0.14247824103213824, 0.1834433114532673, 0.5580453433480241, + 14.600000000000001, 0.8016850514199598, -0.13998626586096757, 0.17924455647213433, 0.5527895735948594, + 14.700000000000001, 0.8082665744348602, -0.13735437396038203, 0.1750662647059742, 0.5451520187683302, + 14.8, 0.815958794652413, -0.1347339830027881, 0.1707888929516153, 0.5356203443652485, + 14.9, 0.8243455214793896, -0.13227097071139943, 0.16630011421907387, 0.5246933614356678, + 15, 0.8330296784122903, -0.13010487883197763, 0.1614946627764397, 0.5128876575656282, + 15.100000000000001, 0.8416404977499994, -0.1283673003157531, 0.1562731143458356, 0.5007412730037657, + 15.200000000000001, 0.8498373085262991, -0.12717952435781404, 0.15053963149684488, 0.4888146243410583, + 15.3, 0.8573096709144672, -0.12664946548780592, 0.14419874955876893, 0.4776889800632504, + 15.4, 0.863773614446678, -0.12686786799921362, 0.13715132248918915, 0.46796282095324865, + 15.5, 0.8689637830367286, -0.1279036957419353, 0.12928981708504877, 0.46024638139171353, + 15.600000000000001, 0.8729171080126423, -0.1297201756476976, 0.12057759333635522, 0.4545871121732205, + 15.700000000000001, 0.8761232275660844, -0.1321559625327215, 0.11110413302438643, 0.45010972363540036, + 15.8, 0.8786761985609237, -0.13516548327459846, 0.1008496253467368, 0.44664055266073993, + 15.9, 0.8806491769745997, -0.13870931045799106, 0.08978709734858797, 0.44402143128000493, + 16, 0.8821066692978742, -0.1427515976892017, 0.07788680662745101, 0.44208986721114657, + 16.1, 0.8831054266583971, -0.1472605886491663, 0.06511764464319697, 0.44067994825418794, + 16.2, 0.8837078730928298, -0.15088285816503716, 0.05265098684329775, 0.4399120727260424, + 16.3, 0.8839756660312651, -0.15339333115777418, 0.04127023919055074, 0.43972067858818875, + 16.400000000000002, 0.8839920740365718, -0.15576376699014038, 0.030268201837494693, 0.4397493580337398, + 16.5, 0.8838519347610062, -0.15799241473807155, 0.019648220371435807, 0.43983872242072414, + 16.6, 0.8835529799612515, -0.16008706696173503, 0.009380921455918484, 0.44002075054003903, + 16.7, 0.8826008577909064, -0.16205300106944306, -0.0007431995821290671, 0.44130941336609236, + 16.8, 0.8810494712786278, -0.16386324793469445, -0.010735449882067441, 0.44360502167105037, + 16.900000000000002, 0.8790639516216942, -0.1655013842461443, -0.02056209396042035, 0.44657928866420193, + 17, 0.8768105474041924, -0.16696584238613746, -0.03018557317273661, 0.44990499286991864, + 17.1, 0.8744586315449575, -0.1682691424520501, -0.039565127841200344, 0.4532573199341422, + 17.2, 0.8721814936211576, -0.16943706217551108, -0.04865742662488005, 0.45631456143798765, + 17.3, 0.8701559309962738, -0.17050772527968305, -0.05741710882958388, 0.45875815740497494, + 17.400000000000002, 0.8685606578960501, -0.17153058886752898, -0.06579716806652397, 0.4602720644492398, + 17.5, 0.8675735472259526, -0.17256531896260704, -0.07374910228233754, 0.46054144304032074, + 17.6, 0.8673743939646827, -0.17368171610208083, -0.08122024420757107, 0.45923805821937075, + 17.7, 0.8684404153380028, -0.17501698688707856, -0.08803767985860061, 0.45544447107593694, + 17.8, 0.870691834717031, -0.1766215598726277, -0.09422705412842129, 0.44924583004801466, + 17.900000000000002, 0.8737147071100198, -0.17846523031108308, -0.09993983088282996, 0.44134431270015945, + 18, 0.8771072836310416, -0.18049478304196895, -0.10533131443902503, 0.4324462515672384, + 18.1, 0.880491043404794, -0.18263750013008803, -0.11056226837764327, 0.42326711523736305, + 18.2, 0.8835159219474432, -0.18480326707412884, -0.11580019210979567, 0.41453309114161463, + 18.3, 0.8858596050942937, -0.18688520630825559, -0.12121998322367324, 0.4069796007081374, + 18.400000000000002, 0.8872207566340081, -0.18875873410071947, -0.12700374267828704, 0.40134712985666104, + 18.5, 0.8870791545579698, -0.19076184818232517, -0.13579931687269858, 0.3978178431942057, + 18.6, 0.8850536612404124, -0.19272232114643476, -0.14793522501063572, 0.39705578053709845, + 18.7, 0.88210630077556, -0.194207262623056, -0.1601489710718591, 0.39815112751459303, + 18.8, 0.8799275325870334, -0.1958521457738144, -0.17153904877234388, 0.39742147543554757, + 18.900000000000002, 0.8779714033314864, -0.19745852417743223, -0.18229337009013863, 0.3961634427644611, + 19, 0.8756248784029074, -0.19872965126325515, -0.19265212338966797, 0.3958190967906057, + 19.1, 0.8722756376006697, -0.19930567091898782, -0.20287131983684337, 0.39781363623628385, + 19.200000000000003, 0.8672846158630236, -0.19876895196443956, -0.21321505419064768, 0.403543850763167, + 19.3, 0.8599360705550634, -0.1966458701686457, -0.22394007156774787, 0.41435636914395957, + 19.400000000000002, 0.8493668441165748, -0.19240513476387552, -0.23527135988846543, 0.43151316949564494, + 19.5, 0.8344778558518805, -0.1854537381320896, -0.24736758494139047, 0.4561391202538394, + 19.6, 0.8138331617890655, -0.1751331039979646, -0.2602752053456938, 0.4891429219959213, + 19.700000000000003, 0.7846921563021088, -0.1602749902411784, -0.27413089040122474, 0.5323743065447176, + 19.8, 0.7389768993844568, -0.13737863441334214, -0.28974746908648674, 0.5925256594788408, + 19.900000000000002, 0.6750481961363872, -0.10682129821630587, -0.3049006935376056, 0.6632757422224032, + 20, 0.5940311129406909, -0.07035920241521051, -0.31706221507474086, 0.7359675069361677, + 20.1, 0.49921326878608474, -0.03041054265566663, -0.3243719925853828, 0.8028973294198258, + 20.200000000000003, 0.3961542702604587, 0.010143176784476916, -0.32599575155576904, 0.8583039555348161, + 20.3, 0.2921847408614224, 0.04830681924681446, -0.3222813157939426, 0.8991269553917979, + 20.400000000000002, 0.1955430875518302, 0.08133868730874558, -0.3146692062326356, 0.9252730459202357, + 20.5, 0.11449183809537226, 0.10699605996341494, -0.30537918267051706, 0.9392481125630943, + 20.6, 0.056730430364313564, 0.12356498961250467, -0.2969448152876766, 0.9451651328135782, + 20.700000000000003, 0.029275779846792895, 0.12965568878616782, -0.2916747921433784, 0.9472371121888455, + 20.8, 0.03713389867781724, 0.12635804209110568, -0.2921778928608792, 0.9472522355166362, + 20.900000000000002, 0.07597486091524928, 0.11626199827658162, -0.2985312466644934, 0.9442404688587478, + 21, 0.13809564624398693, 0.0980726783405286, -0.30698648209693713, 0.9365204974058591, + 21.1, 0.2160069962986056, 0.07352075896027499, -0.31557930273311663, 0.9210566645100828, + 21.200000000000003, 0.30190091499697236, 0.04465081882520493, -0.32257138759005644, 0.8959965635037085, + 21.3, 0.3880480097987539, 0.013856246649106339, -0.3268286709706944, 0.8616320365164905, + 21.400000000000002, 0.4674060006995052, -0.016242437669675466, -0.32805498945281186, 0.8207604629998672, + 21.5, 0.5341643580927372, -0.04299764863987044, -0.3268360484558285, 0.7784586297193934, + 21.6, 0.583930097245805, -0.06390653571517327, -0.3244770837588107, 0.7413880349309447, + 21.700000000000003, 0.6133504709693217, -0.07666120784091272, -0.32265180358692974, 0.7168124389379588, + 21.8, 0.621958018449096, -0.0803248930330997, -0.3225752576924416, 0.7089861338353171, + 21.900000000000002, 0.6161623508530293, -0.07749869189042609, -0.32394782923306553, 0.713719632686201, + 22, 0.5987818678760578, -0.06949223115501779, -0.32630953377168004, 0.7281161944921056, + 22.1, 0.5721094209977494, -0.05751165138303021, -0.3290562116675161, 0.7490695761568744, + 22.200000000000003, 0.5384909869391269, -0.04280194510281597, -0.3315933839047045, 0.7734605860878548, + 22.3, 0.5005641970320234, -0.026667101984526674, -0.33346725031974106, 0.7984509648598489, + 22.400000000000002, 0.4613606205932492, -0.010461178506956628, -0.33444743228093926, 0.8216945031766638, + 22.5, 0.4242898526070885, 0.004440299258745161, -0.3345570346580271, 0.8414451825745842, + 22.6, 0.3930403756574744, 0.016672925854721195, -0.3340471482336593, 0.8565359183381828, + 22.700000000000003, 0.3714366240421479, 0.024904106043190398, -0.3333160674227681, 0.8662072610063842, + 22.8, 0.36313752924371157, 0.02788653084494616, -0.3327713088599097, 0.8698371872091174, + 22.900000000000002, 0.3682991841869814, 0.025650895570142863, -0.33255765687910166, 0.8678151573547993, + 23, 0.3837282891686025, 0.019409484008025245, -0.33250699248083365, 0.8612868116798437, + 23.1, 0.4061014216001908, 0.011811994916959969, -0.3346319562128909, 0.8502726421751554, + 23.200000000000003, 0.4319814043056616, 0.005021521823946826, -0.3407741666569788, 0.8350088730019113, + 23.3, 0.4579234745532665, -0.0030372654407598053, -0.34561895966802897, 0.8190509148946088, + 23.400000000000002, 0.4809366150289463, -0.010900110344773, -0.34925770194332406, 0.8041145549934033, + 23.5, 0.49823837621255784, -0.01708669324327141, -0.35188194378322984, 0.7922409122371932, + 23.6, 0.5071728764347497, -0.02013019311016587, -0.35374452464889816, 0.7856432141978793, + 23.700000000000003, 0.5050420306443867, -0.018595674388908807, -0.3550624689239858, 0.7864587664577335, + 23.8, 0.4888541242880317, -0.011089079437157097, -0.3558660608155668, 0.7964031794533655, + 23.900000000000002, 0.4551259990956647, 0.003683534343208996, -0.35579926509224974, 0.8162436152777738, + 24, 0.40448506040674054, 0.024827186932872787, -0.3540391485260097, 0.8428711218257116, + 24.1, 0.3395075246888604, 0.05045824431228341, -0.3496984654972196, 0.8717221974284324, + 24.200000000000003, 0.26331924792778416, 0.07855305890666925, -0.3421520232567473, 0.8985679626986852, + 24.3, 0.1797678420847072, 0.10703642545960439, -0.3312362043974822, 0.9200594021438983, + 24.400000000000002, 0.09334580708495738, 0.1339178717270794, -0.3173458612355177, 0.9341542529413625, + 24.5, 0.008929181420615459, 0.15743732687999507, -0.3014209898354472, 0.9403611777985406, + 24.6, -0.06856092880708597, 0.1761738037431548, -0.28483245437072435, 0.9397513835333937, + 24.700000000000003, -0.13445019898925947, 0.1890780764457467, -0.269191865177845, 0.9347236836206719, + 24.8, -0.1844488952510596, 0.19540989180766533, -0.25611767231783816, 0.9285458077827247, + 24.900000000000002, -0.21695728385984536, 0.19522229536700392, -0.24648066382101372, 0.9241564124833789, + 25, -0.23742182018270397, 0.19029255356642694, -0.2392816974360784, 0.9220433247035679, + 25.1, -0.24817996780549975, 0.18134025179357888, -0.23391223773957104, 0.9223922602099225, + 25.200000000000003, -0.2512186580514651, 0.16888383631518725, -0.22969945251773854, 0.9249895119358583, + 25.3, -0.24847676839161237, 0.15335871087230413, -0.2259239688622168, 0.9293539485377948, + 25.400000000000002, -0.2414609977516161, 0.1369973269449718, -0.2225555680185413, 0.9345572952573165, + 25.5, -0.23093370345476796, 0.12560423030256035, -0.22114544884970683, 0.9391421044718571, + 25.6, -0.2201030141646485, 0.11448712959395849, -0.2195740075408293, 0.9435224509917253, + 25.700000000000003, -0.21101101540317968, 0.10406663845153304, -0.21760711093421214, 0.9472547869558552, + 25.8, -0.20572692670036777, 0.09474797015341663, -0.21509469471365236, 0.9499650130864132, + 25.900000000000002, -0.20580422297193918, 0.086794121449366, -0.21200122808527924, 0.9514025864895905, + 26, -0.20728257418722573, 0.0792359304564174, -0.20878097102925258, 0.9524526801364493, + 26.1, -0.20890788832354473, 0.07174516708550173, -0.20554676763262783, 0.9533942791478502, + 26.200000000000003, -0.21070518066482474, 0.06428321024022157, -0.20228427205686308, 0.9542285203244765, + 26.3, -0.2126992009237447, 0.056810627411537244, -0.1989791442440866, 0.9549549218132752, + 26.400000000000002, -0.21491454768667917, 0.049286474031641846, -0.19561684122378234, 0.9555713642108516, + 26.5, -0.217375773819389, 0.04166753982343298, -0.19218239539726165, 0.9560740117694106, + 26.6, -0.22010748364665567, 0.0339075222896977, -0.18866017799553458, 0.9564571672655311, + 26.700000000000003, -0.2231344233468726, 0.02595609599759116, -0.18503363742516746, 0.9567130516614866, + 26.8, -0.22648156335239047, 0.01775784878038131, -0.18128500839584702, 0.9568314930012849, + 26.900000000000002, -0.2301745562745589, 0.009251121387412579, -0.17739497541910848, 0.9567994111060789, + 27, -0.2345459223607131, 0.00042202518547417717, -0.17334218076283361, 0.9565252325825444, + 27.1, -0.2397371710465477, -0.008791985912791449, -0.16911302665432565, 0.9559547970579577, + 27.200000000000003, -0.24555276960992437, -0.018525540427990428, -0.16469021860172983, 0.9551113932864903, + 27.3, -0.251797734122386, -0.028923557847150054, -0.16004518209898655, 0.9540161783634473, + 27.400000000000002, -0.25827801281407164, -0.04014371067597356, -0.1551361401900636, 0.9526876343255993, + 27.5, -0.26480075153268695, -0.05235927649664873, -0.1499054748629852, 0.9511400615884995, + 27.6, -0.27117441154437866, -0.06576235904986227, -0.1442762249620151, 0.9493809148947893, + 27.700000000000003, -0.27745494584286867, -0.08210432753008022, -0.13750603658799546, 0.9472960056550567, + 27.8, -0.2839231108466924, -0.10489679161076483, -0.12809332231540582, 0.9444450386417974, + 27.900000000000002, -0.28884331329282215, -0.12451042845215224, -0.11999652641157305, 0.9416302497381034, + 28, -0.2936596721843858, -0.14157818476138417, -0.11302663291958004, 0.9385864876414388, + 28.1, -0.30196080211805887, -0.15632410580422967, -0.10753231007080652, 0.9342479597085361, + 28.200000000000003, -0.3124386997786558, -0.16943556755397898, -0.10323370814226623, 0.9289867861447122, + 28.3, -0.32346700138116535, -0.18150092889350564, -0.09972873119921363, 0.923298809704457, + 28.400000000000002, -0.33343981129173417, -0.19301947501110728, -0.09655271344948832, 0.9177357724518044, + 28.5, -0.3407787519282435, -0.20443523332521302, -0.09318187866864103, 0.9129037271789819, + 28.6, -0.34392839231451144, -0.21615582773983438, -0.08902544395037218, 0.9094308051872345, + 28.700000000000003, -0.34134025573588067, -0.22855732608046547, -0.08340670274301326, 0.907905116433788, + 28.8, -0.3314479714099161, -0.241974739098902, -0.07553201172098656, 0.9087823628854439, + 28.900000000000002, -0.31264040199391196, -0.256676127145548, -0.06444545052285347, 0.9122609981258402, + 29, -0.2828810033276124, -0.27283526786250323, -0.04885705284212366, 0.9182332181719094, + 29.1, -0.23754408311458275, -0.29047509152540096, -0.026340989906403156, 0.9265544679247482, + 29.200000000000003, -0.17849451517445208, -0.3089355588869762, 0.004035906226898256, 0.9341746303388657, + 29.3, -0.10974403675336492, -0.3275321980072597, 0.04292767455713392, 0.9374625968126549, + 29.400000000000002, -0.03601004133140633, -0.345892906326113, 0.09144763968288808, 0.933112374514996, + 29.5, 0.03706564918267122, -0.3641920311550104, 0.15171395686383027, 0.9181357075000269, + 29.6, 0.10249196669818912, -0.38332932771198647, 0.2277924299554667, 0.8891932479125095, + 29.700000000000003, 0.15087636350782774, -0.4046948384535172, 0.32677174069651416, 0.8406418025210889, + 29.8, 0.16881151361728666, -0.42808331370548564, 0.45697830616533924, 0.7611952292884058, + 29.900000000000002, 0.1423466349130348, -0.44576957339756496, 0.6122577860814612, 0.6373125813480508, + 30, 0.09343174999255299, -0.4420021955623113, 0.7327630102924036, 0.5088840122843713 + ] + } + } +] \ No newline at end of file diff --git a/Apps/Sandcastle/gallery/Entity tracking.html b/Apps/Sandcastle/gallery/Entity tracking.html new file mode 100644 index 000000000000..c92d8f90a445 --- /dev/null +++ b/Apps/Sandcastle/gallery/Entity tracking.html @@ -0,0 +1,123 @@ + + + + + + + + + Cesium Demo + + + + + + +
+

Loading...

+
+ + + diff --git a/Apps/Sandcastle/gallery/Entity tracking.jpg b/Apps/Sandcastle/gallery/Entity tracking.jpg new file mode 100644 index 0000000000000000000000000000000000000000..4fe477db6de51d55552a67d122f165faef4ce93e GIT binary patch literal 16430 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 literal 0 HcmV?d00001 diff --git a/packages/engine/Source/Core/TrackingReferenceFrame.js b/packages/engine/Source/Core/TrackingReferenceFrame.js index 29294e828e5e..5ca7dce4053d 100644 --- a/packages/engine/Source/Core/TrackingReferenceFrame.js +++ b/packages/engine/Source/Core/TrackingReferenceFrame.js @@ -5,19 +5,36 @@ */ const TrackingReferenceFrame = { /** - * The east-north-up entity's frame. + * Auto-detect algorithm. The reference frame used to track the Entity will + * be automatically selected based on its trajectory: near-surface slow moving + * objects will be tracked in the entity's local east-north-up reference + * frame, while faster objects like satellites will use VVLH (Vehicle Velocity, + * Local Horizontal). * * @type {number} * @constant */ - EAST_NORTH_UP: 0, + AUTODETECT: 0, /** - * The entity's inertial frame. + * The entity's inertial reference frame. If entity has no defined orientation + * property, a {@link VelocityOrientationProperty} is used instead, thus + * falling back to TrackingReferenceFrame.VELOCITY. + * When selected, the auto-detect algorithm is overridden. * * @type {number} * @constant */ INERTIAL: 1, + + /** + * The entity's inertial reference frame with orientation fixed to its + * {@link VelocityOrientationProperty}, ignoring its own orientation. + * When selected, the auto-detect algorithm is overridden. + * + * @type {number} + * @constant + */ + VELOCITY: 2, }; export default Object.freeze(TrackingReferenceFrame); diff --git a/packages/engine/Source/DataSources/Entity.js b/packages/engine/Source/DataSources/Entity.js index d0ff82b413df..7e789d6a046a 100644 --- a/packages/engine/Source/DataSources/Entity.js +++ b/packages/engine/Source/DataSources/Entity.js @@ -74,7 +74,10 @@ function createPropertyTypeDescriptor(name, Type) { * @property {string} [name] A human readable name to display to users. It does not have to be unique. * @property {TimeIntervalCollection} [availability] The availability, if any, associated with this object. * @property {boolean} [show] A boolean value indicating if the entity and its children are displayed. - * @property {TrackingReferenceFrame} [trackingReferenceFrame=TrackingReferenceFrame.EAST_NORTH_UP] The reference frame used when this entity is being tracked. If undefined, east-north-up at entity's position is used. When set to TrackingReferenceFrame.INERTIAL, the camera will track and rotate according to entity's position and orirentation. + * @property {TrackingReferenceFrame} [trackingReferenceFrame=TrackingReferenceFrame.AUTODETECT] The reference frame used when this entity is being tracked. + * If undefined, an auto-detect algorithm is used: near-surface slow moving entities are tracked using the local + * east-north-up reference frame, whereas fast moving entities such as satellites are tracked using VVLH (Vehicle Velocity, + * Local Horizontal). * @property {Property | string} [description] A string Property specifying an HTML description for this entity. * @property {PositionProperty | Cartesian3 | CallbackPositionProperty} [position] A Property specifying the entity position. * @property {Property | Quaternion} [orientation=Transforms.eastNorthUpToFixedFrame(position)] A Property specifying the entity orientation in respect to Earth-fixed-Earth-centered (ECEF). If undefined, east-north-up at entity position is used. @@ -126,7 +129,7 @@ function Entity(options) { this._show = defaultValue(options.show, true); this._trackingReferenceFrame = defaultValue( options.trackingReferenceFrame, - TrackingReferenceFrame.EAST_NORTH_UP + TrackingReferenceFrame.AUTODETECT ); this._parent = undefined; this._propertyNames = [ @@ -304,7 +307,7 @@ Object.defineProperties(Entity.prototype, { }, /** * Gets or sets the entity's tracking reference frame. - * @demo {@link https://sandcastle.cesium.com/index.html?src=Interpolation.html|Cesium Sandcastle Interpolation Demo} + * @demo {@link https://sandcastle.cesium.com/index.html?src=Entity tracking.html|Cesium Sandcastle Entity tracking Demo} * * @memberof Entity.prototype * @type {TrackingReferenceFrame} diff --git a/packages/engine/Source/DataSources/EntityView.js b/packages/engine/Source/DataSources/EntityView.js index c4fac8c8a7e2..46e85bdd35f2 100644 --- a/packages/engine/Source/DataSources/EntityView.js +++ b/packages/engine/Source/DataSources/EntityView.js @@ -12,6 +12,8 @@ import Quaternion from "../Core/Quaternion.js"; import TrackingReferenceFrame from "../Core/TrackingReferenceFrame.js"; import Transforms from "../Core/Transforms.js"; import SceneMode from "../Scene/SceneMode.js"; +import VelocityOrientationProperty from "./VelocityOrientationProperty.js"; +import VelocityVectorProperty from "./VelocityVectorProperty.js"; const updateTransformMatrix3Scratch1 = new Matrix3(); const updateTransformMatrix3Scratch2 = new Matrix3(); @@ -24,6 +26,8 @@ const updateTransformCartesian3Scratch4 = new Cartesian3(); const updateTransformCartesian3Scratch5 = new Cartesian3(); const updateTransformCartesian3Scratch6 = new Cartesian3(); const updateTransformOrientationScratch = new Quaternion(); +const velocityScratch = new Cartesian3(); +const rotationScratch = new Matrix3(); const deltaTime = new JulianDate(); const northUpAxisFactor = 1.25; // times ellipsoid's maximum radius @@ -33,6 +37,7 @@ function updateTransform( updateLookAt, saveCamera, positionProperty, + velocityProperty, orientationProperty, trackingReferenceFrame, time, @@ -242,6 +247,8 @@ function updateTransform( ); } + const velocity = velocityProperty.getValue(time, velocityScratch); + if ( trackingReferenceFrame === TrackingReferenceFrame.INERTIAL && defined(orientation) @@ -252,6 +259,17 @@ function updateTransform( Cartesian3.ONE, transform ); + } else if ( + trackingReferenceFrame === TrackingReferenceFrame.VELOCITY && + defined(velocity) + ) { + const rotation = Transforms.rotationMatrixFromPositionVelocity( + cartesian, + velocity, + ellipsoid, + rotationScratch + ); + Matrix4.fromRotationTranslation(rotation, cartesian, transform); } else if (hasBasis) { transform[0] = xBasis.x; transform[1] = xBasis.y; @@ -274,20 +292,13 @@ function updateTransform( Transforms.eastNorthUpToFixedFrame(cartesian, ellipsoid, transform); } - if ( - trackingReferenceFrame === TrackingReferenceFrame.EAST_NORTH_UP || - mode === SceneMode.SCENE2D - ) { - camera._setTransform(transform); + camera._setTransform(transform); - if (saveCamera) { - Cartesian3.clone(position, camera.position); - Cartesian3.clone(direction, camera.direction); - Cartesian3.clone(up, camera.up); - Cartesian3.cross(direction, up, camera.right); - } - } else { - camera.lookAtTransform(transform, position); + if (saveCamera) { + Cartesian3.clone(position, camera.position); + Cartesian3.clone(direction, camera.direction); + Cartesian3.clone(up, camera.up); + Cartesian3.cross(direction, up, camera.right); } } @@ -347,6 +358,8 @@ function EntityView(entity, scene, ellipsoid) { this._lastCartesian = new Cartesian3(); this._defaultOffset3D = undefined; + this._velocityProperty = new VelocityVectorProperty(entity.position, true); + this._offset3D = new Cartesian3(); } @@ -398,12 +411,13 @@ EntityView.prototype.update = function (time, boundingSphere) { if (!defined(positionProperty)) { return; } - const orientationProperty = entity.orientation; + const velocityProperty = this._velocityProperty; + let orientationProperty = entity.orientation; if ( trackingReferenceFrame === TrackingReferenceFrame.INERTIAL && !defined(orientationProperty) ) { - return; + orientationProperty = new VelocityOrientationProperty(positionProperty); } const objectChanged = entity !== this._lastEntity; const sceneModeChanged = sceneMode !== this._mode; @@ -458,6 +472,7 @@ EntityView.prototype.update = function (time, boundingSphere) { updateLookAt, saveCamera, positionProperty, + velocityProperty, orientationProperty, trackingReferenceFrame, time, diff --git a/packages/engine/Specs/DataSources/EntitySpec.js b/packages/engine/Specs/DataSources/EntitySpec.js index 3f3c87728625..8b2af579fe1c 100644 --- a/packages/engine/Specs/DataSources/EntitySpec.js +++ b/packages/engine/Specs/DataSources/EntitySpec.js @@ -56,7 +56,7 @@ describe("DataSources/Entity", function () { expect(entity.wall).toBeUndefined(); expect(entity.entityCollection).toBeUndefined(); expect(entity.trackingReferenceFrame).toBe( - TrackingReferenceFrame.EAST_NORTH_UP + TrackingReferenceFrame.AUTODETECT ); const options = { diff --git a/packages/engine/Specs/DataSources/EntityViewSpec.js b/packages/engine/Specs/DataSources/EntityViewSpec.js index 17ea37b0221f..c9d0c5392d7d 100644 --- a/packages/engine/Specs/DataSources/EntityViewSpec.js +++ b/packages/engine/Specs/DataSources/EntityViewSpec.js @@ -79,6 +79,10 @@ describe( entity.trackingReferenceFrame = TrackingReferenceFrame.INERTIAL; view.update(JulianDate.now()); expect(view.scene.camera.position).toEqualEpsilon(sampleOffset, 1e-10); + + entity.trackingReferenceFrame = TrackingReferenceFrame.VELOCITY; + view.update(JulianDate.now()); + expect(view.scene.camera.position).toEqualEpsilon(sampleOffset, 1e-10); }); it("uses entity bounding sphere", function () { @@ -104,6 +108,13 @@ describe( new BoundingSphere(new Cartesian3(3, 4, 5), 6) ); expect(view.scene.camera.position).toEqualEpsilon(sampleOffset, 1e-10); + + entity.trackingReferenceFrame = TrackingReferenceFrame.VELOCITY; + view.update( + JulianDate.now(), + new BoundingSphere(new Cartesian3(3, 4, 5), 6) + ); + expect(view.scene.camera.position).toEqualEpsilon(sampleOffset, 1e-10); }); it("uses entity viewFrom if available and boundingsphere is supplied", function () { @@ -125,6 +136,10 @@ describe( entity.trackingReferenceFrame = TrackingReferenceFrame.INERTIAL; view.update(JulianDate.now()); expect(view.scene.camera.position).toEqualEpsilon(sampleOffset, 1e-10); + + entity.trackingReferenceFrame = TrackingReferenceFrame.VELOCITY; + view.update(JulianDate.now()); + expect(view.scene.camera.position).toEqualEpsilon(sampleOffset, 1e-10); }); it("update throws without time parameter", function () { From 4b3303deba21e9942a53816ec6fc917ae29c6163 Mon Sep 17 00:00:00 2001 From: "jerome.fayot" Date: Tue, 29 Oct 2024 17:23:35 +0100 Subject: [PATCH 048/175] fix: INERTIAL tracking without orientation property now falls back to AUTODETECT --- packages/engine/Source/DataSources/EntityView.js | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/packages/engine/Source/DataSources/EntityView.js b/packages/engine/Source/DataSources/EntityView.js index 46e85bdd35f2..bc759b53dbd6 100644 --- a/packages/engine/Source/DataSources/EntityView.js +++ b/packages/engine/Source/DataSources/EntityView.js @@ -12,7 +12,6 @@ import Quaternion from "../Core/Quaternion.js"; import TrackingReferenceFrame from "../Core/TrackingReferenceFrame.js"; import Transforms from "../Core/Transforms.js"; import SceneMode from "../Scene/SceneMode.js"; -import VelocityOrientationProperty from "./VelocityOrientationProperty.js"; import VelocityVectorProperty from "./VelocityVectorProperty.js"; const updateTransformMatrix3Scratch1 = new Matrix3(); @@ -243,7 +242,7 @@ function updateTransform( if (defined(orientationProperty)) { orientation = orientationProperty.getValue( time, - updateTransformOrientationScratch + updateTransformOrientationScratch, ); } @@ -257,7 +256,7 @@ function updateTransform( cartesian, orientation, Cartesian3.ONE, - transform + transform, ); } else if ( trackingReferenceFrame === TrackingReferenceFrame.VELOCITY && @@ -267,7 +266,7 @@ function updateTransform( cartesian, velocity, ellipsoid, - rotationScratch + rotationScratch, ); Matrix4.fromRotationTranslation(rotation, cartesian, transform); } else if (hasBasis) { @@ -412,13 +411,7 @@ EntityView.prototype.update = function (time, boundingSphere) { return; } const velocityProperty = this._velocityProperty; - let orientationProperty = entity.orientation; - if ( - trackingReferenceFrame === TrackingReferenceFrame.INERTIAL && - !defined(orientationProperty) - ) { - orientationProperty = new VelocityOrientationProperty(positionProperty); - } + const orientationProperty = entity.orientation; const objectChanged = entity !== this._lastEntity; const sceneModeChanged = sceneMode !== this._mode; From 32c8e63add40d3cc5a9621bb64aa35a9bdb4485d Mon Sep 17 00:00:00 2001 From: "jerome.fayot" Date: Tue, 29 Oct 2024 17:24:06 +0100 Subject: [PATCH 049/175] test: removed obsolete test case --- packages/engine/Specs/DataSources/EntityViewSpec.js | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/packages/engine/Specs/DataSources/EntityViewSpec.js b/packages/engine/Specs/DataSources/EntityViewSpec.js index c9d0c5392d7d..2aa12913f9e5 100644 --- a/packages/engine/Specs/DataSources/EntityViewSpec.js +++ b/packages/engine/Specs/DataSources/EntityViewSpec.js @@ -105,14 +105,14 @@ describe( entity.trackingReferenceFrame = TrackingReferenceFrame.INERTIAL; view.update( JulianDate.now(), - new BoundingSphere(new Cartesian3(3, 4, 5), 6) + new BoundingSphere(new Cartesian3(3, 4, 5), 6), ); expect(view.scene.camera.position).toEqualEpsilon(sampleOffset, 1e-10); entity.trackingReferenceFrame = TrackingReferenceFrame.VELOCITY; view.update( JulianDate.now(), - new BoundingSphere(new Cartesian3(3, 4, 5), 6) + new BoundingSphere(new Cartesian3(3, 4, 5), 6), ); expect(view.scene.camera.position).toEqualEpsilon(sampleOffset, 1e-10); }); @@ -156,13 +156,6 @@ describe( const view = new EntityView(entity, scene); view.update(JulianDate.now()); }); - - it("update returns with entity.trackingReferenceFrame set to INERTIAL and without entity.orientation property.", function () { - const entity = new Entity(); - entity.trackingReferenceFrame = TrackingReferenceFrame.INERTIAL; - const view = new EntityView(entity, scene); - view.update(JulianDate.now()); - }); }, "WebGL", ); From e69487a58b6b985682c1dcf8638f65e9f01c0cf1 Mon Sep 17 00:00:00 2001 From: "jerome.fayot" Date: Tue, 29 Oct 2024 17:28:43 +0100 Subject: [PATCH 050/175] fix: fixed prettier --- Apps/Sandcastle/gallery/Entity tracking.html | 35 ++++++------------- Apps/Sandcastle/gallery/Interpolation.html | 6 ++-- packages/engine/Source/DataSources/Entity.js | 2 +- .../engine/Specs/DataSources/EntitySpec.js | 2 +- 4 files changed, 15 insertions(+), 30 deletions(-) diff --git a/Apps/Sandcastle/gallery/Entity tracking.html b/Apps/Sandcastle/gallery/Entity tracking.html index c92d8f90a445..f5f21aa22a47 100644 --- a/Apps/Sandcastle/gallery/Entity tracking.html +++ b/Apps/Sandcastle/gallery/Entity tracking.html @@ -1,4 +1,4 @@ - + @@ -21,10 +21,7 @@ > - + @@ -45,16 +42,12 @@ const startTime = Cesium.JulianDate.fromIso8601("2012-03-15T10:00:00Z"); - const satelliteStopTime = Cesium.JulianDate.fromIso8601( - "2012-03-16T10:00:00Z" - ); + const satelliteStopTime = Cesium.JulianDate.fromIso8601("2012-03-16T10:00:00Z"); - const droneStopTime = Cesium.JulianDate.fromIso8601( - "2012-03-15T10:00:30Z" - ); + const droneStopTime = Cesium.JulianDate.fromIso8601("2012-03-15T10:00:30Z"); const dataSource = await viewer.dataSources.add( - Cesium.CzmlDataSource.load("../../SampleData/tracking.czml") + Cesium.CzmlDataSource.load("../../SampleData/tracking.czml"), ); const satellite = dataSource.entities.getById("Satellite/ISS"); @@ -83,28 +76,22 @@ { text: "Tracking reference frame: Auto-detect", onselect: function () { - satellite.trackingReferenceFrame = - Cesium.TrackingReferenceFrame.AUTODETECT; - drone.trackingReferenceFrame = - Cesium.TrackingReferenceFrame.AUTODETECT; + satellite.trackingReferenceFrame = Cesium.TrackingReferenceFrame.AUTODETECT; + drone.trackingReferenceFrame = Cesium.TrackingReferenceFrame.AUTODETECT; }, }, { text: "Tracking reference frame: Inertial", onselect: function () { - satellite.trackingReferenceFrame = - Cesium.TrackingReferenceFrame.INERTIAL; - drone.trackingReferenceFrame = - Cesium.TrackingReferenceFrame.INERTIAL; + satellite.trackingReferenceFrame = Cesium.TrackingReferenceFrame.INERTIAL; + drone.trackingReferenceFrame = Cesium.TrackingReferenceFrame.INERTIAL; }, }, { text: "Tracking reference frame: Velocity", onselect: function () { - satellite.trackingReferenceFrame = - Cesium.TrackingReferenceFrame.VELOCITY; - drone.trackingReferenceFrame = - Cesium.TrackingReferenceFrame.VELOCITY; + satellite.trackingReferenceFrame = Cesium.TrackingReferenceFrame.VELOCITY; + drone.trackingReferenceFrame = Cesium.TrackingReferenceFrame.VELOCITY; }, }, ]); diff --git a/Apps/Sandcastle/gallery/Interpolation.html b/Apps/Sandcastle/gallery/Interpolation.html index 0c01e6547df7..567ec12627ea 100644 --- a/Apps/Sandcastle/gallery/Interpolation.html +++ b/Apps/Sandcastle/gallery/Interpolation.html @@ -158,15 +158,13 @@ { text: "Tracking reference frame: East-North-Up", onselect: function () { - entity.trackingReferenceFrame = - Cesium.TrackingReferenceFrame.EAST_NORTH_UP; + entity.trackingReferenceFrame = Cesium.TrackingReferenceFrame.EAST_NORTH_UP; }, }, { text: "Tracking reference frame: Inertial", onselect: function () { - entity.trackingReferenceFrame = - Cesium.TrackingReferenceFrame.INERTIAL; + entity.trackingReferenceFrame = Cesium.TrackingReferenceFrame.INERTIAL; }, }, ]); diff --git a/packages/engine/Source/DataSources/Entity.js b/packages/engine/Source/DataSources/Entity.js index 7e789d6a046a..b57d2568c1da 100644 --- a/packages/engine/Source/DataSources/Entity.js +++ b/packages/engine/Source/DataSources/Entity.js @@ -129,7 +129,7 @@ function Entity(options) { this._show = defaultValue(options.show, true); this._trackingReferenceFrame = defaultValue( options.trackingReferenceFrame, - TrackingReferenceFrame.AUTODETECT + TrackingReferenceFrame.AUTODETECT, ); this._parent = undefined; this._propertyNames = [ diff --git a/packages/engine/Specs/DataSources/EntitySpec.js b/packages/engine/Specs/DataSources/EntitySpec.js index 8b2af579fe1c..33ce89a6d98e 100644 --- a/packages/engine/Specs/DataSources/EntitySpec.js +++ b/packages/engine/Specs/DataSources/EntitySpec.js @@ -56,7 +56,7 @@ describe("DataSources/Entity", function () { expect(entity.wall).toBeUndefined(); expect(entity.entityCollection).toBeUndefined(); expect(entity.trackingReferenceFrame).toBe( - TrackingReferenceFrame.AUTODETECT + TrackingReferenceFrame.AUTODETECT, ); const options = { From d779bc186441d9661a1e64fa4e08c81bd7abab45 Mon Sep 17 00:00:00 2001 From: jjspace <8007967+jjspace@users.noreply.github.com> Date: Mon, 11 Nov 2024 14:25:55 -0500 Subject: [PATCH 051/175] oauth testing web app and service app --- .prettierignore | 1 + Apps/Sandcastle/gallery/iTwin Demo.html | 80 ++++++++++++++++- eslint.config.js | 8 ++ itwin-oauth-demo/.gitignore | 1 + itwin-oauth-demo/index.html | 34 +++++++ itwin-oauth-demo/server.js | 114 ++++++++++++++++++++++++ packages/engine/Source/Core/ITwin.js | 5 +- 7 files changed, 238 insertions(+), 5 deletions(-) create mode 100644 itwin-oauth-demo/.gitignore create mode 100644 itwin-oauth-demo/index.html create mode 100644 itwin-oauth-demo/server.js diff --git a/.prettierignore b/.prettierignore index c46eee96cb50..e0d51a3709d0 100644 --- a/.prettierignore +++ b/.prettierignore @@ -11,6 +11,7 @@ !packages/**/ !Specs/**/ !Tools/**/ +!itwin-oauth-demo/**/ !**/*.js !**/*.cjs diff --git a/Apps/Sandcastle/gallery/iTwin Demo.html b/Apps/Sandcastle/gallery/iTwin Demo.html index d8d4865927e2..ef4991471367 100644 --- a/Apps/Sandcastle/gallery/iTwin Demo.html +++ b/Apps/Sandcastle/gallery/iTwin Demo.html @@ -30,15 +30,87 @@ window.startup = async function (Cesium) { "use strict"; //Sandcastle_Begin + const popup = (url) => { + // based on https://gist.github.com/gauravtiwari/2ae9f44aee281c759fe5a66d5c2721a2 + const windowArea = { + width: 500, + height: 600, + }; + + windowArea.left = Math.floor( + window.screenX + (window.outerWidth - windowArea.width) / 2, + ); + windowArea.top = Math.floor( + window.screenY + (window.outerHeight - windowArea.height) / 8, + ); + + const sep = url.indexOf("?") !== -1 ? "&" : "?"; + const fullUrl = `${url}${sep}`; + const windowOpts = `toolbar=0,scrollbars=1,status=1,resizable=1,location=1,menuBar=0, + width=${windowArea.width},height=${windowArea.height}, + left=${windowArea.left},top=${windowArea.top}`; + + const authWindow = window.open(fullUrl, "popup", windowOpts); + + // Listen to message from child window + const authPromise = new Promise((resolve, reject) => { + window.addEventListener( + "message", + (e) => { + console.log("message", e); + // TODO: if we go this route we may want this back to make sure + // it's coming from wherever we host this + // if (e.origin !== window.SITE_DOMAIN) { + // authWindow.close(); + // reject("Not allowed"); + // } + + if (e.data.auth) { + resolve(e.data.auth); + authWindow.close(); + } else { + authWindow.close(); + reject("Unauthorised"); + } + }, + false, + ); + }); + return authPromise; + }; + + // ===== Web app oauth flow ===== + + // const clientId = "webapp-0MsbOTn56gKXMoI1WsJ0SoVEn"; + // const redirectUri = "http://localhost:3000"; + + // const result = await popup( + // `https://ims.bentley.com/connect/authorize?response_type=code&client_id=${clientId}&redirect_uri=${redirectUri}&scope=itwin-platform`, + // ).catch((error) => { + // console.error(error); + // throw new Error("OAuth failed"); + // }); + // const accessCode = result.code; + // console.log("popup returned", result); + + // ===== Service app request ===== + + const serviceResponse = await fetch("http://localhost:3000/service"); + const serviceResult = await serviceResponse.json(); + const { token: accessCode } = serviceResult; + // must be created for the Start export route if you want to create new exports // Needs to have the mesh-export:modify scope not just mesh-export:read - const accessToken = + let accessToken = "Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IkJlbnRsZXlJTVNfMjAyNCIsInBpLmF0bSI6ImE4bWUifQ.eyJzY29wZSI6WyJtZXNoLWV4cG9ydDpyZWFkIiwibWVzaC1leHBvcnQ6bW9kaWZ5Il0sImNsaWVudF9pZCI6Iml0d2luLWRldmVsb3Blci1jb25zb2xlIiwiYXVkIjpbImh0dHBzOi8vaW1zLmJlbnRsZXkuY29tL2FzL3Rva2VuLm9hdXRoMiIsImh0dHBzOi8vaW1zb2lkYy5iZW50bGV5LmNvbS9hcy90b2tlbi5vYXV0aDIiLCJodHRwczovL2ltc29pZGMuYmVudGxleS5jb20vcmVzb3VyY2VzIiwiYmVudGxleS1hcGktbWFuYWdlbWVudCJdLCJzdWIiOiJjMWM1MzRhNy0zZDk2LTQ2MzMtYjY5ZC1jMGEzNzE5OWQwZGUiLCJyb2xlIjoiQkVOVExFWV9FTVBMT1lFRSIsIm9yZyI6ImZhYjk3NzRiLWIzMzgtNGNjMi1hNmM5LTQ1OGJkZjdmOTY2YSIsInN1YmplY3QiOiJjMWM1MzRhNy0zZDk2LTQ2MzMtYjY5ZC1jMGEzNzE5OWQwZGUiLCJpc3MiOiJodHRwczovL2ltcy5iZW50bGV5LmNvbSIsImVudGl0bGVtZW50IjpbIkJFTlRMRVlfTEVBUk4iLCJJTlRFUk5BTCIsIlNFTEVDVF8yMDA2IiwiQkVOIiwiQkROIl0sInByZWZlcnJlZF91c2VybmFtZSI6Ikpvc2guUm91emVyQGJlbnRsZXkuY29tIiwiZ2l2ZW5fbmFtZSI6Ikpvc2giLCJzaWQiOiJHMGZTamlOXzBMRjE1Vy1IYnRTaWtoeDNuSXMuU1UxVExVSmxiblJzWlhrdFZWTS5LbWlWLlF2UExVcGJyc2R3engwWGxPbkF3eTFTT0IiLCJuYmYiOjE3MzA4MzgyNDIsInVsdGltYXRlX3NpdGUiOiIxMDAxMzg5MTE3IiwidXNhZ2VfY291bnRyeV9pc28iOiJVUyIsImF1dGhfdGltZSI6MTczMDgzODU0MiwibmFtZSI6Ikpvc2guUm91emVyQGJlbnRsZXkuY29tIiwib3JnX25hbWUiOiJCZW50bGV5IFN5c3RlbXMgSW5jIiwiZmFtaWx5X25hbWUiOiJSb3V6ZXIiLCJlbWFpbCI6Ikpvc2guUm91emVyQGJlbnRsZXkuY29tIiwiZXhwIjoxNzMwODQyMTQyfQ.N_WrgjL2bqxdNLEM5nHh4Fg-FzeA-qxxpryaoaMKz8onnpgzmp-X8dyQ1TyMRqKQY99iLypE9bU45DwRJs7vBgNQ73d53SkC9TKGYn8AAOTJz8c_oEHgkoTEDaaLsMPCw88tqZ34pY0e0oIHIofVDTCvwlzwaJPADfkIxz-8GzhIt2WrXR7f6LWBFSlWNrtNhIr9Tz7UNaLOh97_3fS9KVU1I084CpBga9cj_mjGBeki7mIEQvpqMj8x2bJPae_c6WrPEjKLayrOzq4q0X0Kvle0ZFAm-Se9MFCusTFS51bYrI3IsjagGeIP2U06zzcMJ22NylomE60hz6GK_4uB3g"; + + accessToken = `Bearer ${accessCode}`; + Cesium.ITwin.defaultAccessToken = accessToken; // this is the iModel in the "Hello iTwinCesium" iTwin that we should all have access to // https://developer.bentley.com/my-itwins/b4a30036-0456-49ea-a439-3fcd9365e24e/home/ - const imodelId = "2852c3d7-00c3-4b5d-a0ce-82bbde4f061e"; - // const imodelId = "88673c1d-12b8-48f1-8beb-5000d0edbd0b"; + // const imodelId = "2852c3d7-00c3-4b5d-a0ce-82bbde4f061e"; + const imodelId = "88673c1d-12b8-48f1-8beb-5000d0edbd0b"; const changesetId = ""; @@ -107,7 +179,7 @@ const subcategory = feature.getProperty("subcategory"); const message = ` Element ID: ${element} - Subcategory: ${classes[subcategory] ?? subcategory} + Subcategory: ${classes[subcategory] || subcategory} Feature ID: ${feature.featureId}`; nameOverlay.textContent = message; } diff --git a/eslint.config.js b/eslint.config.js index c8ec3d9bd70c..84dd74b032c4 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -103,4 +103,12 @@ export default [ "n/no-missing-import": "off", }, }, + { + files: ["itwin-oauth-demo/*"], + languageOptions: { + ...configCesium.configs.node.languageOptions, + sourceType: "module", + ecmaVersion: 2022, + }, + }, ]; diff --git a/itwin-oauth-demo/.gitignore b/itwin-oauth-demo/.gitignore new file mode 100644 index 000000000000..d344ba6b06cb --- /dev/null +++ b/itwin-oauth-demo/.gitignore @@ -0,0 +1 @@ +config.json diff --git a/itwin-oauth-demo/index.html b/itwin-oauth-demo/index.html new file mode 100644 index 000000000000..af6bc98d6499 --- /dev/null +++ b/itwin-oauth-demo/index.html @@ -0,0 +1,34 @@ + + + + + + Auth! + + + + + diff --git a/itwin-oauth-demo/server.js b/itwin-oauth-demo/server.js new file mode 100644 index 000000000000..790b96c9958a --- /dev/null +++ b/itwin-oauth-demo/server.js @@ -0,0 +1,114 @@ +import express from "express"; +import { readFileSync, writeFileSync } from "fs"; +import { dirname, join } from "path"; +import { exit } from "process"; +import { fileURLToPath } from "url"; + +let config = { + webapp: { + clientId: "", + clientSecret: "", + }, + serviceapp: { + clientId: "", + clientSecret: "", + }, + port: 3000, + redirectUri: "http://localhost:3000", +}; + +const __dirname = dirname(fileURLToPath(import.meta.url)); +const configPath = join(__dirname, "./config.json"); +try { + const configFile = readFileSync(configPath, { encoding: "utf-8" }); + config = JSON.parse(configFile); +} catch { + console.log("config file missing, default written to", configPath); + console.log("Please update the config with the desired values"); + writeFileSync(configPath, JSON.stringify(config, undefined, 2)); + exit(1); +} + +const app = express(); +const port = config.port ?? 3000; +const redirectUri = config.redirectUri ?? "http://localhost:3000"; + +// eslint-disable-next-line no-unused-vars +app.get("/", async (req, res) => { + res.sendFile(join(__dirname, "./index.html")); +}); + +app.get("/token", async (req, res) => { + console.log("/token request received"); + const { code } = req.query; + + if (!code) { + res.status(404).send("Code missing"); + } + + const body = new URLSearchParams(); + body.set("grant_type", "authorization_code"); + body.set("client_id", config.webapp.clientId); + body.set("client_secret", config.webapp.clientSecret); + body.set("code", code); + body.set("redirect_uri", redirectUri); + + const response = await fetch("https://ims.bentley.com/connect/token", { + method: "POST", + body, + }); + + const result = await response.json(); + + if (!response.ok || !result) { + console.log(" bad response/no result"); + res.status(response.status).send(); + return; + } + const { access_token } = result; + if (access_token) { + console.log(" token acquired, returned"); + res.status(200).send({ token: access_token }); + return; + } + console.log(" token not found"); + res.status(404).send("token not found"); +}); + +// eslint-disable-next-line no-unused-vars +app.get("/service", async (req, res) => { + console.log("/service request received"); + + const body = new URLSearchParams(); + body.set("grant_type", "client_credentials"); + body.set("client_id", config.serviceapp.clientId); + body.set("client_secret", config.serviceapp.clientSecret); + body.set("scope", "itwin-platform"); + + const response = await fetch("https://ims.bentley.com/connect/token", { + method: "POST", + body, + }); + + const result = await response.json(); + + res.setHeader("Access-Control-Allow-Origin", "*"); + + if (!response.ok || !result) { + console.log(" bad response/no result"); + res.status(response.status).send(); + return; + } + const { access_token } = result; + if (access_token) { + console.log(" token acquired, returned"); + res.status(200).send({ token: access_token }); + return; + } + console.log(" token not found"); + res.status(404).send("token not found"); +}); + +app.listen(port, () => { + console.log(`Server listening on port ${port}`); +}); diff --git a/packages/engine/Source/Core/ITwin.js b/packages/engine/Source/Core/ITwin.js index e7d49d3c59c1..e3ed43b1ee68 100644 --- a/packages/engine/Source/Core/ITwin.js +++ b/packages/engine/Source/Core/ITwin.js @@ -161,7 +161,7 @@ ITwin.getExports = async function (iModelId, changesetId) { }; // obtain export for specified export id - let url = `${ITwin.apiEndpoint}mesh-export/?iModelId=${iModelId}`; + let url = `${ITwin.apiEndpoint}mesh-export/?iModelId=${iModelId}&exportType=CESIUM&$top=1`; if (defined(changesetId) && changesetId !== "") { url += `&changesetId=${changesetId}`; } @@ -173,6 +173,9 @@ ITwin.getExports = async function (iModelId, changesetId) { throw new RuntimeError( `Unauthorized, bad token, wrong scopes or headers bad. ${result.error.details[0].code}`, ); + } else if (response.status === 403) { + console.error(result.error.code, result.error.message); + throw new RuntimeError("Not allowed, forbidden"); } else if (response.status === 422) { throw new RuntimeError( `Unprocessable Entity:${result.error.code} ${result.error.message}`, From 9557a3eb0b61f0644093cb0983858be2c4a6cce4 Mon Sep 17 00:00:00 2001 From: Mark Dane Date: Mon, 11 Nov 2024 15:42:36 -0500 Subject: [PATCH 052/175] Add option to select which geocoder is used --- .../engine/Source/Core/IonGeocodeProvider.js | 33 ++++++++++ .../engine/Source/Core/IonGeocoderService.js | 62 +++++++++++++++++++ .../Specs/Core/IonGeocoderServiceSpec.js | 46 ++++++++++++++ packages/widgets/Source/Viewer/Viewer.js | 12 +++- packages/widgets/Specs/Viewer/ViewerSpec.js | 15 +++++ 5 files changed, 166 insertions(+), 2 deletions(-) create mode 100644 packages/engine/Source/Core/IonGeocodeProvider.js diff --git a/packages/engine/Source/Core/IonGeocodeProvider.js b/packages/engine/Source/Core/IonGeocodeProvider.js new file mode 100644 index 000000000000..bd61230859df --- /dev/null +++ b/packages/engine/Source/Core/IonGeocodeProvider.js @@ -0,0 +1,33 @@ +/** + * Underlying geocoding services that can be used via Cesium ion. + * + * @enum {string} + */ +const IonGeocodeProvider = Object.freeze({ + /** + * Google geocoder, for use with Google data. + * + * @type {string} + * @constant + */ + GOOGLE: "GOOGLE", + + /** + * Bing geocoder, for use with Bing data. + * + * @type {string} + * @constant + */ + BING: "BING", + + /** + * Use the default geocoder as set on the server. Used when neither Bing or + * Google data is used. + * + * @type {string} + * @constant + */ + DEFAULT: "DEFAULT", +}); + +export default IonGeocodeProvider; diff --git a/packages/engine/Source/Core/IonGeocoderService.js b/packages/engine/Source/Core/IonGeocoderService.js index 81da96b7dbba..f19bf7aca7c7 100644 --- a/packages/engine/Source/Core/IonGeocoderService.js +++ b/packages/engine/Source/Core/IonGeocoderService.js @@ -2,10 +2,43 @@ import Check from "./Check.js"; import Credit from "./Credit.js"; import defaultValue from "./defaultValue.js"; import defined from "./defined.js"; +import DeveloperError from "./DeveloperError.js"; import Ion from "./Ion.js"; +import IonGeocodeProvider from "./IonGeocodeProvider.js"; import PeliasGeocoderService from "./PeliasGeocoderService.js"; import Resource from "./Resource.js"; +/** + * @param {*} geocodeProvider + * @throws {DeveloperError} + * @private + */ +function validateIonGeocodeProvider(geocodeProvider) { + if ( + !Object.values(IonGeocodeProvider).some( + (value) => value === geocodeProvider, + ) + ) { + throw new DeveloperError(`Invalid geocodeProvider: "${geocodeProvider}"`); + } +} + +const providerToParameterMap = Object.freeze({ + [IonGeocodeProvider.GOOGLE]: "google", + [IonGeocodeProvider.BING]: "bing", + [IonGeocodeProvider.DEFAULT]: undefined, +}); + +function providerToQueryParameter(provider) { + return providerToParameterMap[provider]; +} + +function queryParameterToProvider(parameter) { + return Object.entries(providerToParameterMap).find( + (entry) => entry[1] === parameter, + )[0]; +} + /** * Provides geocoding through Cesium ion. * @alias IonGeocoderService @@ -15,6 +48,7 @@ import Resource from "./Resource.js"; * @param {Scene} options.scene The scene * @param {string} [options.accessToken=Ion.defaultAccessToken] The access token to use. * @param {string|Resource} [options.server=Ion.defaultServer] The resource to the Cesium ion API server. + * @param {IonGeocodeProvider} [options.geocodeProvider=IonGeocodeProvider.DEFAULT] The geocoder the Cesium ion API server should use to fulfill this request. * * @see Ion */ @@ -25,6 +59,12 @@ function IonGeocoderService(options) { Check.typeOf.object("options.scene", options.scene); //>>includeEnd('debug'); + const geocodeProvider = defaultValue( + options.geocodeProvider, + IonGeocodeProvider.DEFAULT, + ); + validateIonGeocodeProvider(geocodeProvider); + const accessToken = defaultValue(options.accessToken, Ion.defaultAccessToken); const server = Resource.createIfNeeded( defaultValue(options.server, Ion.defaultServer), @@ -40,6 +80,9 @@ function IonGeocoderService(options) { const searchEndpoint = server.getDerivedResource({ url: "v1/geocode", + queryParameters: { + geocoder: providerToQueryParameter(geocodeProvider), + }, }); if (defined(accessToken)) { @@ -64,6 +107,25 @@ Object.defineProperties(IonGeocoderService.prototype, { return undefined; }, }, + /** + * @memberof IonGeocoderService.prototype + * @type {IonGeocodeProvider} + */ + geocodeProvider: { + get: function () { + return queryParameterToProvider( + this._pelias.url.queryParameters["geocoder"], + ); + }, + set: function (geocodeProvider) { + validateIonGeocodeProvider(geocodeProvider); + const query = { + ...this._pelias.url.queryParameters, + geocoder: providerToQueryParameter(geocodeProvider), + }; + this._pelias.url.setQueryParameters(query); + }, + }, }); /** diff --git a/packages/engine/Specs/Core/IonGeocoderServiceSpec.js b/packages/engine/Specs/Core/IonGeocoderServiceSpec.js index 17951c615c02..c38574084f41 100644 --- a/packages/engine/Specs/Core/IonGeocoderServiceSpec.js +++ b/packages/engine/Specs/Core/IonGeocoderServiceSpec.js @@ -1,7 +1,9 @@ import { + DeveloperError, GeocoderService, GeocodeType, Ion, + IonGeocodeProvider, IonGeocoderService, } from "../../index.js"; @@ -26,20 +28,24 @@ describe("Core/IonGeocoderService", function () { expect(service._accessToken).toEqual(Ion.defaultAccessToken); expect(service._server.url).toEqual(Ion.defaultServer.url); + expect(service.geocodeProvider).toEqual(IonGeocodeProvider.DEFAULT); }); it("creates with specified parameters", function () { const accessToken = "123456"; const server = "http://not.ion.invalid/"; + const geocodeProvider = IonGeocodeProvider.GOOGLE; const service = new IonGeocoderService({ accessToken: accessToken, server: server, scene: scene, + geocodeProvider, }); expect(service._accessToken).toEqual(accessToken); expect(service._server.url).toEqual(server); + expect(service.geocodeProvider).toEqual(geocodeProvider); }); it("calls inner geocoder and returns result", async function () { @@ -64,4 +70,44 @@ describe("Core/IonGeocoderService", function () { expect(service.credit).toBeUndefined(); }); + + it("setting geocodeProvider updates _pelias.url for GOOGLE", function () { + const service = new IonGeocoderService({ + scene, + geocoder: IonGeocodeProvider.DEFAULT, + }); + + service.geocodeProvider = IonGeocodeProvider.GOOGLE; + expect(service._pelias.url.queryParameters["geocoder"]).toEqual("google"); + }); + + it("setting geocodeProvider updates _pelias.url for BING", function () { + const service = new IonGeocoderService({ + scene, + geocoder: IonGeocodeProvider.DEFAULT, + }); + + service.geocodeProvider = IonGeocodeProvider.BING; + expect(service._pelias.url.queryParameters["geocoder"]).toEqual("bing"); + }); + + it("setting geocodeProvider updates _pelias.url for DEFAULT", function () { + const service = new IonGeocoderService({ + scene, + geocoder: IonGeocodeProvider.GOOGLE, + }); + + service.geocodeProvider = IonGeocodeProvider.DEFAULT; + expect(service._pelias.url.queryParameters["geocoder"]).toBeUndefined(); + }); + + it("throws if setting invalid geocodeProvider", function () { + expect( + () => new IonGeocoderService({ scene, geocodeProvider: "junk" }), + ).toThrowError(DeveloperError, /Invalid geocodeProvider/); + expect(() => { + const service = new IonGeocoderService({ scene }); + service.geocodeProvider = "junk"; + }).toThrowError(DeveloperError, /Invalid geocodeProvider/); + }); }); diff --git a/packages/widgets/Source/Viewer/Viewer.js b/packages/widgets/Source/Viewer/Viewer.js index 4574ef6aadc7..f7503b537d15 100644 --- a/packages/widgets/Source/Viewer/Viewer.js +++ b/packages/widgets/Source/Viewer/Viewer.js @@ -18,6 +18,7 @@ import { Math as CesiumMath, Property, ScreenSpaceEventType, + IonGeocoderService, } from "@cesium/engine"; import Animation from "../Animation/Animation.js"; import AnimationViewModel from "../Animation/AnimationViewModel.js"; @@ -280,7 +281,7 @@ function enableVRUI(viewer, enabled) { * @property {boolean} [baseLayerPicker=true] If set to false, the BaseLayerPicker widget will not be created. * @property {boolean} [fullscreenButton=true] If set to false, the FullscreenButton widget will not be created. * @property {boolean} [vrButton=false] If set to true, the VRButton widget will be created. - * @property {boolean|GeocoderService[]} [geocoder=true] If set to false, the Geocoder widget will not be created. + * @property {boolean|IonGeocodeProvider|GeocoderService[]} [geocoder=IonGeocodeProvider.DEFAULT] If set to false, the Geocoder widget will not be created. * @property {boolean} [homeButton=true] If set to false, the HomeButton widget will not be created. * @property {boolean} [infoBox=true] If set to false, the InfoBox widget will not be created. * @property {boolean} [sceneModePicker=true] If set to false, the SceneModePicker widget will not be created. @@ -557,7 +558,14 @@ Either specify options.terrainProvider instead or set options.baseLayerPicker to geocoderContainer.className = "cesium-viewer-geocoderContainer"; toolbar.appendChild(geocoderContainer); let geocoderService; - if (defined(options.geocoder) && typeof options.geocoder !== "boolean") { + if (typeof options.geocoder === "string") { + geocoderService = [ + new IonGeocoderService({ scene, geocodeProvider: options.geocoder }), + ]; + } else if ( + defined(options.geocoder) && + typeof options.geocoder !== "boolean" + ) { geocoderService = Array.isArray(options.geocoder) ? options.geocoder : [options.geocoder]; diff --git a/packages/widgets/Specs/Viewer/ViewerSpec.js b/packages/widgets/Specs/Viewer/ViewerSpec.js index 49df43f46eb2..8bb508d100b7 100644 --- a/packages/widgets/Specs/Viewer/ViewerSpec.js +++ b/packages/widgets/Specs/Viewer/ViewerSpec.js @@ -18,6 +18,8 @@ import { ImageryLayerCollection, SceneMode, ShadowMode, + IonGeocodeProvider, + IonGeocoderService, } from "@cesium/engine"; import { @@ -354,6 +356,19 @@ describe( expect(viewer.geocoder.viewModel._geocoderServices.length).toBe(1); }); + it("constructs geocoder with IonGeocodeProvider", function () { + viewer = createViewer(container, { + geocoder: IonGeocodeProvider.GOOGLE, + }); + expect(viewer.geocoder).toBeDefined(); + expect(viewer.geocoder.viewModel._geocoderServices.length).toBe(1); + const geocoderService = viewer.geocoder.viewModel._geocoderServices[0]; + expect(geocoderService).toBeInstanceOf(IonGeocoderService); + expect(geocoderService.geocodeProvider).toEqual( + IonGeocodeProvider.GOOGLE, + ); + }); + it("constructs geocoder with geocoder service option", function () { const service = new CartographicGeocoderService(); viewer = createViewer(container, { From 3bd5d7297213067e67d8dea313ab90ccaf4af846 Mon Sep 17 00:00:00 2001 From: Mark Dane Date: Mon, 11 Nov 2024 16:50:54 -0500 Subject: [PATCH 053/175] Fix bug with gecoder:undefined getting passed in the query --- packages/engine/Source/Core/IonGeocoderService.js | 8 +++++--- packages/engine/Specs/Core/IonGeocoderServiceSpec.js | 5 ++++- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/packages/engine/Source/Core/IonGeocoderService.js b/packages/engine/Source/Core/IonGeocoderService.js index f19bf7aca7c7..eb5d5a973ae0 100644 --- a/packages/engine/Source/Core/IonGeocoderService.js +++ b/packages/engine/Source/Core/IonGeocoderService.js @@ -80,9 +80,6 @@ function IonGeocoderService(options) { const searchEndpoint = server.getDerivedResource({ url: "v1/geocode", - queryParameters: { - geocoder: providerToQueryParameter(geocodeProvider), - }, }); if (defined(accessToken)) { @@ -92,6 +89,7 @@ function IonGeocoderService(options) { this._accessToken = accessToken; this._server = server; this._pelias = new PeliasGeocoderService(searchEndpoint); + this.geocodeProvider = geocodeProvider; } Object.defineProperties(IonGeocoderService.prototype, { @@ -123,6 +121,10 @@ Object.defineProperties(IonGeocoderService.prototype, { ...this._pelias.url.queryParameters, geocoder: providerToQueryParameter(geocodeProvider), }; + // Delete the geocoder parameter to prevent sending &geocoder=undefined in the query + if (!defined(query.geocoder)) { + delete query.geocoder; + } this._pelias.url.setQueryParameters(query); }, }, diff --git a/packages/engine/Specs/Core/IonGeocoderServiceSpec.js b/packages/engine/Specs/Core/IonGeocoderServiceSpec.js index c38574084f41..0bdc02b18692 100644 --- a/packages/engine/Specs/Core/IonGeocoderServiceSpec.js +++ b/packages/engine/Specs/Core/IonGeocoderServiceSpec.js @@ -98,7 +98,10 @@ describe("Core/IonGeocoderService", function () { }); service.geocodeProvider = IonGeocodeProvider.DEFAULT; - expect(service._pelias.url.queryParameters["geocoder"]).toBeUndefined(); + const queryParameters = service._pelias.url.queryParameters; + expect(queryParameters.geocoder).toBeUndefined(); + // Make sure that it isn't 'geocoder: undefined' + expect(queryParameters.hasOwnProperty("geocoder")).toBeFalse(); }); it("throws if setting invalid geocodeProvider", function () { From 5f849b1f1da94460d36432a2d777f5169df4df57 Mon Sep 17 00:00:00 2001 From: Mark Dane Date: Mon, 11 Nov 2024 16:57:41 -0500 Subject: [PATCH 054/175] Update documentation --- .../gallery/3D Tiles Vertical Exaggeration.html | 1 + Apps/Sandcastle/gallery/AEC Clipping.html | 1 + Apps/Sandcastle/gallery/Bing Maps Labels Only.html | 1 + Apps/Sandcastle/gallery/Clamp Entities to Ground.html | 1 + Apps/Sandcastle/gallery/Globe Interior.html | 1 + ...ogle Photorealistic 3D Tiles with Building Insert.html | 1 + .../gallery/Google Photorealistic 3D Tiles.html | 1 + Apps/Sandcastle/gallery/Imagery Color To Alpha.html | 4 +++- Apps/Sandcastle/gallery/Imagery Layers Manipulation.html | 1 + .../Source/Scene/createGooglePhotorealistic3DTileset.js | 8 ++++++-- 10 files changed, 17 insertions(+), 3 deletions(-) diff --git a/Apps/Sandcastle/gallery/3D Tiles Vertical Exaggeration.html b/Apps/Sandcastle/gallery/3D Tiles Vertical Exaggeration.html index 4e636b2f001c..36f9cba9d603 100644 --- a/Apps/Sandcastle/gallery/3D Tiles Vertical Exaggeration.html +++ b/Apps/Sandcastle/gallery/3D Tiles Vertical Exaggeration.html @@ -78,6 +78,7 @@

Loading...

animation: false, sceneModePicker: false, baseLayerPicker: false, + geocoder: Cesium.IonGeocodeProvider.GOOGLE, // The globe does not need to be displayed, // since the Photorealistic 3D Tiles include terrain globe: false, diff --git a/Apps/Sandcastle/gallery/AEC Clipping.html b/Apps/Sandcastle/gallery/AEC Clipping.html index 0fa60bf14964..6c5ba834e71c 100644 --- a/Apps/Sandcastle/gallery/AEC Clipping.html +++ b/Apps/Sandcastle/gallery/AEC Clipping.html @@ -32,6 +32,7 @@ animation: false, sceneModePicker: false, baseLayerPicker: false, + geocoder: Cesium.IonGeocodeProvider.GOOGLE, // The globe does not need to be displayed, // since the Photorealistic 3D Tiles include terrain globe: false, diff --git a/Apps/Sandcastle/gallery/Bing Maps Labels Only.html b/Apps/Sandcastle/gallery/Bing Maps Labels Only.html index f4c12739f24f..8bebfcd63b1a 100644 --- a/Apps/Sandcastle/gallery/Bing Maps Labels Only.html +++ b/Apps/Sandcastle/gallery/Bing Maps Labels Only.html @@ -67,6 +67,7 @@ baseLayer: false, baseLayerPicker: false, infoBox: false, + geocoder: Cesium.IonGeocodeProvider.BING, }); const layers = viewer.imageryLayers; diff --git a/Apps/Sandcastle/gallery/Clamp Entities to Ground.html b/Apps/Sandcastle/gallery/Clamp Entities to Ground.html index 0a048fbe0d93..52b7bbef1fa9 100644 --- a/Apps/Sandcastle/gallery/Clamp Entities to Ground.html +++ b/Apps/Sandcastle/gallery/Clamp Entities to Ground.html @@ -41,6 +41,7 @@ timeline: false, animation: false, baseLayerPicker: false, + geocoder: Cesium.IonGeocodeProvider.GOOGLE, }); const scene = viewer.scene; scene.globe.depthTestAgainstTerrain = true; diff --git a/Apps/Sandcastle/gallery/Globe Interior.html b/Apps/Sandcastle/gallery/Globe Interior.html index 0deb07a51e34..2c02fa55daf3 100644 --- a/Apps/Sandcastle/gallery/Globe Interior.html +++ b/Apps/Sandcastle/gallery/Globe Interior.html @@ -34,6 +34,7 @@ //Sandcastle_Begin const viewer = new Cesium.Viewer("cesiumContainer", { orderIndependentTranslucency: false, + geocoder: Cesium.IonGeocodeProvider.BING, }); const scene = viewer.scene; diff --git a/Apps/Sandcastle/gallery/Google Photorealistic 3D Tiles with Building Insert.html b/Apps/Sandcastle/gallery/Google Photorealistic 3D Tiles with Building Insert.html index 6437a2fb7c19..feafc0983b21 100644 --- a/Apps/Sandcastle/gallery/Google Photorealistic 3D Tiles with Building Insert.html +++ b/Apps/Sandcastle/gallery/Google Photorealistic 3D Tiles with Building Insert.html @@ -32,6 +32,7 @@ animation: false, sceneModePicker: false, baseLayerPicker: false, + geocoder: Cesium.IonGeocoderProvider.GOOGLE, // The globe does not need to be displayed, // since the Photorealistic 3D Tiles include terrain globe: false, diff --git a/Apps/Sandcastle/gallery/Google Photorealistic 3D Tiles.html b/Apps/Sandcastle/gallery/Google Photorealistic 3D Tiles.html index d4d68c1c97d6..93d337b076a0 100644 --- a/Apps/Sandcastle/gallery/Google Photorealistic 3D Tiles.html +++ b/Apps/Sandcastle/gallery/Google Photorealistic 3D Tiles.html @@ -32,6 +32,7 @@ animation: false, sceneModePicker: false, baseLayerPicker: false, + geocoder: Cesium.IonGeocodeProvider.GOOGLE, // The globe does not need to be displayed, // since the Photorealistic 3D Tiles include terrain globe: false, diff --git a/Apps/Sandcastle/gallery/Imagery Color To Alpha.html b/Apps/Sandcastle/gallery/Imagery Color To Alpha.html index 8fa3ed032883..202c13ddfc3b 100644 --- a/Apps/Sandcastle/gallery/Imagery Color To Alpha.html +++ b/Apps/Sandcastle/gallery/Imagery Color To Alpha.html @@ -51,7 +51,9 @@ window.startup = async function (Cesium) { "use strict"; //Sandcastle_Begin - const viewer = new Cesium.Viewer("cesiumContainer"); + const viewer = new Cesium.Viewer("cesiumContainer", { + geocoder: Cesium.IonGeocodeProvider.BING, + }); const layers = viewer.scene.imageryLayers; diff --git a/Apps/Sandcastle/gallery/Imagery Layers Manipulation.html b/Apps/Sandcastle/gallery/Imagery Layers Manipulation.html index 4e309a339dd7..49df7fd5c6d5 100644 --- a/Apps/Sandcastle/gallery/Imagery Layers Manipulation.html +++ b/Apps/Sandcastle/gallery/Imagery Layers Manipulation.html @@ -106,6 +106,7 @@ //Sandcastle_Begin const viewer = new Cesium.Viewer("cesiumContainer", { baseLayerPicker: false, + geocoder: Cesium.IonGeocodeProvider.BING, }); const imageryLayers = viewer.imageryLayers; diff --git a/packages/engine/Source/Scene/createGooglePhotorealistic3DTileset.js b/packages/engine/Source/Scene/createGooglePhotorealistic3DTileset.js index f762d748dc36..ef35c0c38e31 100644 --- a/packages/engine/Source/Scene/createGooglePhotorealistic3DTileset.js +++ b/packages/engine/Source/Scene/createGooglePhotorealistic3DTileset.js @@ -17,7 +17,9 @@ import Resource from "../Core/Resource.js"; * @see GoogleMaps * * @example - * const viewer = new Cesium.Viewer("cesiumContainer"); + * const viewer = new Cesium.Viewer("cesiumContainer", { + * geocoder: Cesium.IonGeocodeProvider.GOOGLE + * }); * * try { * const tileset = await Cesium.createGooglePhotorealistic3DTileset(); @@ -30,7 +32,9 @@ import Resource from "../Core/Resource.js"; * // Use your own Google Maps API key * Cesium.GoogleMaps.defaultApiKey = "your-api-key"; * - * const viewer = new Cesium.Viewer("cesiumContainer"); + * const viewer = new Cesium.Viewer("cesiumContainer". { + * geocoder: Cesium.IonGeocodeProvider.GOOGLE + * }); * * try { * const tileset = await Cesium.createGooglePhotorealistic3DTileset(); From 096e67b07e374f7727c68d9675c27d0a223ac5c2 Mon Sep 17 00:00:00 2001 From: Mark Dane Date: Tue, 12 Nov 2024 13:01:48 -0500 Subject: [PATCH 055/175] Fix documentation for IonGeocodeProvider --- packages/engine/Source/Core/IonGeocodeProvider.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/engine/Source/Core/IonGeocodeProvider.js b/packages/engine/Source/Core/IonGeocodeProvider.js index bd61230859df..e78d1dce3139 100644 --- a/packages/engine/Source/Core/IonGeocodeProvider.js +++ b/packages/engine/Source/Core/IonGeocodeProvider.js @@ -3,7 +3,7 @@ * * @enum {string} */ -const IonGeocodeProvider = Object.freeze({ +const IonGeocodeProvider = { /** * Google geocoder, for use with Google data. * @@ -28,6 +28,6 @@ const IonGeocodeProvider = Object.freeze({ * @constant */ DEFAULT: "DEFAULT", -}); +}; -export default IonGeocodeProvider; +export default Object.freeze(IonGeocodeProvider); From 0b2e65ebaf0835eb49acae3bcf2d0739f91e2605 Mon Sep 17 00:00:00 2001 From: Mark Dane Date: Tue, 12 Nov 2024 13:04:11 -0500 Subject: [PATCH 056/175] IonGeocoderProvider -> IonGeocodeProvider --- .../Google Photorealistic 3D Tiles with Building Insert.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Apps/Sandcastle/gallery/Google Photorealistic 3D Tiles with Building Insert.html b/Apps/Sandcastle/gallery/Google Photorealistic 3D Tiles with Building Insert.html index feafc0983b21..18c384a7b4d0 100644 --- a/Apps/Sandcastle/gallery/Google Photorealistic 3D Tiles with Building Insert.html +++ b/Apps/Sandcastle/gallery/Google Photorealistic 3D Tiles with Building Insert.html @@ -32,7 +32,7 @@ animation: false, sceneModePicker: false, baseLayerPicker: false, - geocoder: Cesium.IonGeocoderProvider.GOOGLE, + geocoder: Cesium.IonGeocodeProvider.GOOGLE, // The globe does not need to be displayed, // since the Photorealistic 3D Tiles include terrain globe: false, From 79cb58b927ad80222968359dc1160f08aad22026 Mon Sep 17 00:00:00 2001 From: Mark Dane Date: Tue, 12 Nov 2024 14:11:52 -0500 Subject: [PATCH 057/175] Add GoogleGeocoderService --- .../Source/Core/GoogleGeocoderService.js | 107 ++++++++++++++++++ .../Specs/Core/GoogleGeocoderServicesSpec.js | 84 ++++++++++++++ 2 files changed, 191 insertions(+) create mode 100644 packages/engine/Source/Core/GoogleGeocoderService.js create mode 100644 packages/engine/Specs/Core/GoogleGeocoderServicesSpec.js diff --git a/packages/engine/Source/Core/GoogleGeocoderService.js b/packages/engine/Source/Core/GoogleGeocoderService.js new file mode 100644 index 000000000000..b8eab86dba4e --- /dev/null +++ b/packages/engine/Source/Core/GoogleGeocoderService.js @@ -0,0 +1,107 @@ +import Check from "./Check.js"; +import Credit from "./Credit.js"; +import defaultValue from "./defaultValue.js"; +import Rectangle from "./Rectangle.js"; +import Resource from "./Resource.js"; +import defined from "./defined.js"; +import DeveloperError from "./DeveloperError.js"; + +const API_URL = "https://maps.googleapis.com/maps/api/geocode/json"; +const CREDIT_HTML = `Google`; + +/** + * Provides geocoding through Google. + * + * @see {@link https://developers.google.com/maps/documentation/geocoding/policies|Google Geocoding Policies} + * @alias GoogleGeocoderService + * @constructor + * + * @param {object} options Object with the following properties: + * @param {string} options.key An API key to use with the Google geocoding service + */ +function GoogleGeocoderService(options) { + options = defaultValue(options, defaultValue.EMPTY_OBJECT); + const key = options.key; + //>>includeStart('debug', pragmas.debug); + if (!defined(key)) { + throw new DeveloperError("options.key is required."); + } + //>>includeEnd('debug'); + + this._resource = new Resource({ + url: API_URL, + queryParameters: { key }, + }); + + this._credit = new Credit(CREDIT_HTML, true); +} + +Object.defineProperties(GoogleGeocoderService.prototype, { + /** + * Gets the credit to display after a geocode is performed. Typically this is used to credit + * the geocoder service. + * @memberof GoogleGeocoderService.prototype + * @type {Credit|undefined} + * @readonly + */ + credit: { + get: function () { + return this._credit; + }, + }, +}); + +/** + * @function + * + * @param {string} query The query to be sent to the geocoder service + * @returns {Promise} + * @throws {Error} If the services returns a status other than OK or ZERO_RESULTS + */ +GoogleGeocoderService.prototype.geocode = async function (query) { + // See API documentation at https://developers.google.com/maps/documentation/geocoding/requests-geocoding + + //>>includeStart('debug', pragmas.debug); + Check.typeOf.string("query", query); + //>>includeEnd('debug'); + + const resource = this._resource.getDerivedResource({ + queryParameters: { + address: query, + }, + }); + + const response = await resource.fetchJson(); + + if (response.status === "ZERO_RESULTS") { + return []; + } + + if (response.status !== "OK") { + throw new Error( + `GoogleGeocoderService got a bad response ${response.status}: ${response.error_message}`, + ); + } + + const results = response.results.map((result) => { + const southWest = result.geometry.viewport.southwest; + const northEast = result.geometry.viewport.northeast; + return { + displayName: result.formatted_address, + destination: Rectangle.fromDegrees( + southWest.lng, + southWest.lat, + northEast.lng, + northEast.lat, + ), + attribution: { + html: CREDIT_HTML, + collapsible: false, + }, + }; + }); + + return results; +}; + +export default GoogleGeocoderService; diff --git a/packages/engine/Specs/Core/GoogleGeocoderServicesSpec.js b/packages/engine/Specs/Core/GoogleGeocoderServicesSpec.js new file mode 100644 index 000000000000..5260dc265742 --- /dev/null +++ b/packages/engine/Specs/Core/GoogleGeocoderServicesSpec.js @@ -0,0 +1,84 @@ +import { + createGuid, + GeocoderService, + GoogleGeocoderService, + Resource, + Rectangle, +} from "../../index.js"; + +describe("Core/GoogleGeocoderService", function () { + it("conforms to GeocoderService interface", function () { + expect(GoogleGeocoderService).toConformToInterface(GeocoderService); + }); + + it("constructor throws without key", function () { + expect(function () { + return new GoogleGeocoderService({}); + }).toThrowDeveloperError(); + }); + + it("constructor sets key on _resource", function () { + const key = createGuid(); + const service = new GoogleGeocoderService({ key }); + expect(service._resource.toString()).toEqual( + `https://maps.googleapis.com/maps/api/geocode/json?key=${key}`, + ); + }); + + it("geocode returns results for status=OK", async function () { + const key = createGuid(); + const query = createGuid(); + const service = new GoogleGeocoderService({ key }); + + spyOn(Resource.prototype, "fetchJson").and.resolveTo({ + results: [ + { + formatted_address: + "1600 Amphitheatre Pkwy, Mountain View, CA 94043, USA", + geometry: { + viewport: { + northeast: { + lat: 37.4237349802915, + lng: -122.083183169709, + }, + southwest: { + lat: 37.4210370197085, + lng: -122.085881130292, + }, + }, + }, + }, + ], + status: "OK", + }); + + const results = await service.geocode(query); + + expect(results).toEqual([ + { + displayName: "1600 Amphitheatre Pkwy, Mountain View, CA 94043, USA", + destination: Rectangle.fromDegrees( + -122.085881130292, + 37.4210370197085, + -122.083183169709, + 37.4237349802915, + ), + attribution: { + html: `Google`, + collapsible: false, + }, + }, + ]); + }); + + it("returns empty array for status=ZERO_RESULTS", async function () { + const service = new GoogleGeocoderService({ key: "key" }); + + spyOn(Resource.prototype, "fetchJson").and.resolveTo({ + status: "ZERO_RESULTS", + }); + + const results = await service.geocode("test"); + expect(results).toEqual([]); + }); +}); From fa0da08b3f8d22b9ec4fcd3f516ccf7d4f24c67f Mon Sep 17 00:00:00 2001 From: Mark Dane Date: Tue, 12 Nov 2024 14:16:57 -0500 Subject: [PATCH 058/175] Update for Google Geocoder --- CHANGES.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index e3107c706259..21757139473e 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,11 @@ # Change Log +### 1.124 - 2024-12-01 + +##### Additions :tada: + +- Ability to choose between Bing and Google geocoders. Adds `GoogleGeocoderService` for standalone usage of Google geocoder. Updates `Viewer` constructor to also accept `IonGeocoderProvider` [#12299](https://github.com/CesiumGS/cesium/pull/12299) + ### 1.123.1 - 2024-11-07 #### @cesium/engine From cf72b1f5e0d1a6747f9d0f07b2cedfdee3ab118a Mon Sep 17 00:00:00 2001 From: Jeshurun Hembd Date: Tue, 12 Nov 2024 14:43:26 -0500 Subject: [PATCH 059/175] Fix texSubImage2D calls for WebGL1 --- packages/engine/Source/Renderer/Texture.js | 184 +++++++++++---------- 1 file changed, 95 insertions(+), 89 deletions(-) diff --git a/packages/engine/Source/Renderer/Texture.js b/packages/engine/Source/Renderer/Texture.js index b4b1259d5001..99e43f1d0fa2 100644 --- a/packages/engine/Source/Renderer/Texture.js +++ b/packages/engine/Source/Renderer/Texture.js @@ -343,7 +343,7 @@ function loadBufferSource(texture, source) { gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false); gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false); - let arrayBufferView = source.arrayBufferView; + let { arrayBufferView } = source; if (flipY) { arrayBufferView = PixelFormat.flipY( arrayBufferView, @@ -386,6 +386,63 @@ function loadBufferSource(texture, source) { } } +/** + * Load texel data from a buffer into part of a texture + * + * @param {Texture} texture The texture to which texel values will be loaded. + * @param {object} source The source for texel values to be loaded into the texture. + * @param {number} xOffset The texel x coordinate of the lower left corner of the subregion of the texture to be updated. + * @param {number} yOffset The texel y coordinate of the lower left corner of the subregion of the texture to be updated. + * @param {number} width The width of the source data, in pixels. + * @param {number} width The height of the source data, in pixels. + * + * @private + */ +function loadPartialBufferSource( + texture, + source, + xOffset, + yOffset, + width, + height, +) { + const context = texture._context; + const gl = context._gl; + + const { pixelFormat, pixelDatatype } = texture; + + const unpackAlignment = PixelFormat.alignmentInBytes( + pixelFormat, + pixelDatatype, + width, + ); + gl.pixelStorei(gl.UNPACK_ALIGNMENT, unpackAlignment); + gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false); + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false); + + let { arrayBufferView } = source; + if (texture.flipY) { + arrayBufferView = PixelFormat.flipY( + arrayBufferView, + pixelFormat, + pixelDatatype, + width, + height, + ); + } + gl.texSubImage2D( + texture._textureTarget, + 0, + xOffset, + yOffset, + width, + height, + pixelFormat, + PixelDatatype.toWebGLConstant(pixelDatatype, context), + arrayBufferView, + ); +} + /** * Load texel data from a framebuffer into a texture. * @@ -448,6 +505,35 @@ function loadImageSource(texture, source) { ); } +/** + * Load texel data from an Image into part of a texture + * + * @param {Texture} texture The texture to which texel values will be loaded. + * @param {ImageData|HTMLImageElement|HTMLCanvasElement|HTMLVideoElement} source The source for texel values to be loaded into the texture. + * @param {number} xOffset The texel x coordinate of the lower left corner of the subregion of the texture to be updated. + * @param {number} yOffset The texel y coordinate of the lower left corner of the subregion of the texture to be updated. + * + * @private + */ +function loadPartialImageSource(texture, source, xOffset, yOffset) { + const context = texture._context; + const gl = context._gl; + + gl.pixelStorei(gl.UNPACK_ALIGNMENT, 4); + gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, texture.preMultiplyAlpha); + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, texture.flipY); + + gl.texSubImage2D( + texture._textureTarget, + 0, + xOffset, + yOffset, + texture.pixelFormat, + PixelDatatype.toWebGLConstant(texture.pixelDatatype, context), + source, + ); +} + /** * Compute a dimension of the image for the next mip level. * @@ -812,7 +898,7 @@ Texture.prototype.copyFrom = function (options) { gl.bindTexture(target, this._texture); let { width, height } = source; - const arrayBufferView = source.arrayBufferView; + const { arrayBufferView } = source; // Make sure we are using the element's intrinsic width and height where available if (defined(source.videoWidth) && defined(source.videoHeight)) { @@ -823,25 +909,6 @@ Texture.prototype.copyFrom = function (options) { height = source.naturalHeight; } - const textureWidth = this._width; - const textureHeight = this._height; - const internalFormat = this._internalFormat; - const pixelFormat = this._pixelFormat; - const pixelDatatype = this._pixelDatatype; - - const preMultiplyAlpha = this._preMultiplyAlpha; - const flipY = this._flipY; - - let unpackAlignment = 4; - if (defined(arrayBufferView)) { - unpackAlignment = PixelFormat.alignmentInBytes( - pixelFormat, - pixelDatatype, - width, - ); - } - gl.pixelStorei(gl.UNPACK_ALIGNMENT, unpackAlignment); - if (skipColorSpaceConversion) { gl.pixelStorei(gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, gl.NONE); } else { @@ -853,94 +920,33 @@ Texture.prototype.copyFrom = function (options) { let uploaded = false; if (!this._initialized) { - let pixels; if ( xOffset === 0 && yOffset === 0 && - width === textureWidth && - height === textureHeight + width === this._width && + height === this._height ) { // initialize the entire texture if (defined(arrayBufferView)) { - gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false); - gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false); - if (flipY) { - pixels = PixelFormat.flipY( - arrayBufferView, - pixelFormat, - pixelDatatype, - textureWidth, - textureHeight, - ); - } else { - pixels = arrayBufferView; - } + loadBufferSource(this, arrayBufferView); } else { - // Only valid for DOM-Element uploads - gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, preMultiplyAlpha); - gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipY); - pixels = source; + loadImageSource(this, source); } uploaded = true; } else { gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false); gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false); - // initialize the entire texture to zero - pixels = PixelFormat.createTypedArray( - pixelFormat, - pixelDatatype, - textureWidth, - textureHeight, - ); + loadNull(this); } - gl.texImage2D( - target, - 0, - internalFormat, - textureWidth, - textureHeight, - 0, - pixelFormat, - PixelDatatype.toWebGLConstant(pixelDatatype, context), - pixels, - ); this._initialized = true; } if (!uploaded) { - let pixels; if (defined(arrayBufferView)) { - gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false); - gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false); - - if (flipY) { - pixels = PixelFormat.flipY( - arrayBufferView, - pixelFormat, - pixelDatatype, - width, - height, - ); - } else { - pixels = arrayBufferView; - } + loadPartialBufferSource(this, source, xOffset, yOffset, width, height); } else { - // Only valid for DOM-Element uploads - gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, preMultiplyAlpha); - gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipY); - pixels = source; + loadPartialImageSource(this, source, xOffset, yOffset); } - gl.texSubImage2D( - target, - 0, - xOffset, - yOffset, - width, - height, - pixelFormat, - PixelDatatype.toWebGLConstant(pixelDatatype, context), - pixels, - ); } gl.bindTexture(target, null); From 52720962017b1b45db0a5a3f7eda6d302c73c631 Mon Sep 17 00:00:00 2001 From: Jeshurun Hembd Date: Tue, 12 Nov 2024 15:18:54 -0500 Subject: [PATCH 060/175] Update CHANGES.md --- CHANGES.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index e3107c706259..cf681ab8027b 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,13 @@ # Change Log +### 1.124 - 2024-12-02 + +#### @cesium/engine + +##### Fixes :wrench: + +- Fix label rendering bug in WebGL1 contexts. [#12301](https://github.com/CesiumGS/cesium/pull/12301) + ### 1.123.1 - 2024-11-07 #### @cesium/engine From 99d460ed5abbef6257a5c790d5398cedec86f50e Mon Sep 17 00:00:00 2001 From: Jeshurun Hembd Date: Tue, 12 Nov 2024 15:44:27 -0500 Subject: [PATCH 061/175] Fix helper function signature in Texture --- packages/engine/Source/Renderer/Texture.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/engine/Source/Renderer/Texture.js b/packages/engine/Source/Renderer/Texture.js index 99e43f1d0fa2..a59ca336746f 100644 --- a/packages/engine/Source/Renderer/Texture.js +++ b/packages/engine/Source/Renderer/Texture.js @@ -898,7 +898,6 @@ Texture.prototype.copyFrom = function (options) { gl.bindTexture(target, this._texture); let { width, height } = source; - const { arrayBufferView } = source; // Make sure we are using the element's intrinsic width and height where available if (defined(source.videoWidth) && defined(source.videoHeight)) { @@ -927,8 +926,8 @@ Texture.prototype.copyFrom = function (options) { height === this._height ) { // initialize the entire texture - if (defined(arrayBufferView)) { - loadBufferSource(this, arrayBufferView); + if (defined(source.arrayBufferView)) { + loadBufferSource(this, source); } else { loadImageSource(this, source); } @@ -942,7 +941,7 @@ Texture.prototype.copyFrom = function (options) { } if (!uploaded) { - if (defined(arrayBufferView)) { + if (defined(source.arrayBufferView)) { loadPartialBufferSource(this, source, xOffset, yOffset, width, height); } else { loadPartialImageSource(this, source, xOffset, yOffset); From e3274ed7ac84634e2031f2ce5a7b645d12c7afe0 Mon Sep 17 00:00:00 2001 From: Mark Dane Date: Wed, 13 Nov 2024 15:04:13 -0500 Subject: [PATCH 062/175] Fixes unhandled rejection in Skybox.update #12306 --- packages/engine/Source/Scene/SkyBox.js | 13 ++++++++---- packages/engine/Specs/Scene/SkyBoxSpec.js | 25 +++++++++++++++++++++++ 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/packages/engine/Source/Scene/SkyBox.js b/packages/engine/Source/Scene/SkyBox.js index aa175b9d405b..567089583230 100644 --- a/packages/engine/Source/Scene/SkyBox.js +++ b/packages/engine/Source/Scene/SkyBox.js @@ -141,10 +141,15 @@ SkyBox.prototype.update = function (frameState, useHdr) { if (typeof sources.positiveX === "string") { // Given urls for cube-map images. Load them. - loadCubeMap(context, this._sources).then(function (cubeMap) { - that._cubeMap = that._cubeMap && that._cubeMap.destroy(); - that._cubeMap = cubeMap; - }); + loadCubeMap(context, this._sources).then( + function (cubeMap) { + that._cubeMap = that._cubeMap && that._cubeMap.destroy(); + that._cubeMap = cubeMap; + }, + function (error) { + console.error("Skybox cube map failed to load", error); + }, + ); } else { this._cubeMap = this._cubeMap && this._cubeMap.destroy(); this._cubeMap = new CubeMap({ diff --git a/packages/engine/Specs/Scene/SkyBoxSpec.js b/packages/engine/Specs/Scene/SkyBoxSpec.js index 4db7c8702d18..3b28f8190c33 100644 --- a/packages/engine/Specs/Scene/SkyBoxSpec.js +++ b/packages/engine/Specs/Scene/SkyBoxSpec.js @@ -355,6 +355,31 @@ describe( return scene.render(); }).toThrowDeveloperError(); }); + + it("handles error when Resources fail to load", async () => { + spyOn(Resource.prototype, "fetchImage").and.rejectWith( + "intentional error for test", + ); + + skyBox = new SkyBox({ + sources: { + positiveX: "./Data/Images/Blue.png", + negativeX: "./Data/Images/Blue.png", + positiveY: "./Data/Images/Blue.png", + negativeY: "./Data/Images/Blue.png", + positiveZ: "./Data/Images/Blue.png", + negativeZ: "./Data/Images/Blue.png", + }, + }); + + scene.frameState.passes.render = true; + scene.skyBox = skyBox; + skyBox.update(scene.frameState); + + // Flush macro task queue to allow the rejections to be throw in this + // function, otherwise the error ends up happening in `afterAll` + await new Promise((resolve) => window.setTimeout(resolve, 1)); + }); }, "WebGL", ); From f2a7bb907b8809e8bcfe60e2448da06f31058626 Mon Sep 17 00:00:00 2001 From: Mark Dane Date: Wed, 13 Nov 2024 15:13:04 -0500 Subject: [PATCH 063/175] Update changelog for #12307 --- CHANGES.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index e3107c706259..136c6ea70732 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,13 @@ # Change Log +### 1.124.0 + +#### @cesium/engine + +##### Fixes :wrench: + +- Handle uncaught promise rejection in `Skybox.update` that could cause tests to fail[#12307](https://github.com/CesiumGS/cesium/pull/12307) + ### 1.123.1 - 2024-11-07 #### @cesium/engine From 79133ea2abedf760157bf75e874c1eba00539ffd Mon Sep 17 00:00:00 2001 From: Mark Dane Date: Thu, 14 Nov 2024 09:36:45 -0500 Subject: [PATCH 064/175] PR feedback - Throw during next call to update - use catch instead of onRejected - Don't update CHANGES.md for minor bug --- CHANGES.md | 8 ------- packages/engine/Source/Scene/SkyBox.js | 28 +++++++++++++++++------ packages/engine/Specs/Scene/SkyBoxSpec.js | 16 +++++++------ 3 files changed, 30 insertions(+), 22 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 136c6ea70732..e3107c706259 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,13 +1,5 @@ # Change Log -### 1.124.0 - -#### @cesium/engine - -##### Fixes :wrench: - -- Handle uncaught promise rejection in `Skybox.update` that could cause tests to fail[#12307](https://github.com/CesiumGS/cesium/pull/12307) - ### 1.123.1 - 2024-11-07 #### @cesium/engine diff --git a/packages/engine/Source/Scene/SkyBox.js b/packages/engine/Source/Scene/SkyBox.js index 567089583230..c2ba96b72d68 100644 --- a/packages/engine/Source/Scene/SkyBox.js +++ b/packages/engine/Source/Scene/SkyBox.js @@ -81,6 +81,8 @@ function SkyBox(options) { this._attributeLocations = undefined; this._useHdr = undefined; + this._hasError = false; + this._error = false; } /** @@ -111,6 +113,15 @@ SkyBox.prototype.update = function (frameState, useHdr) { return undefined; } + // Throw any errors that had previously occurred asynchronously so they aren't + // ignored when running. See https://github.com/CesiumGS/cesium/pull/12307 + if (this._hasError) { + const error = this._error; + this._hasError = false; + this._error = undefined; + throw error; + } + if (this._sources !== this.sources) { this._sources = this.sources; const sources = this.sources; @@ -141,15 +152,18 @@ SkyBox.prototype.update = function (frameState, useHdr) { if (typeof sources.positiveX === "string") { // Given urls for cube-map images. Load them. - loadCubeMap(context, this._sources).then( - function (cubeMap) { + loadCubeMap(context, this._sources) + .then(function (cubeMap) { that._cubeMap = that._cubeMap && that._cubeMap.destroy(); that._cubeMap = cubeMap; - }, - function (error) { - console.error("Skybox cube map failed to load", error); - }, - ); + }) + .catch((error) => { + // Defer throwing the error until the next call to update to prevent + // test from failing in `afterAll` if this is rejected after the test + // using the Skybox ends. See https://github.com/CesiumGS/cesium/pull/12307 + this._hasError = true; + this._error = error; + }); } else { this._cubeMap = this._cubeMap && this._cubeMap.destroy(); this._cubeMap = new CubeMap({ diff --git a/packages/engine/Specs/Scene/SkyBoxSpec.js b/packages/engine/Specs/Scene/SkyBoxSpec.js index 3b28f8190c33..96ebab2a79d0 100644 --- a/packages/engine/Specs/Scene/SkyBoxSpec.js +++ b/packages/engine/Specs/Scene/SkyBoxSpec.js @@ -356,10 +356,9 @@ describe( }).toThrowDeveloperError(); }); - it("handles error when Resources fail to load", async () => { - spyOn(Resource.prototype, "fetchImage").and.rejectWith( - "intentional error for test", - ); + it("defers throwing error when Resources fail to load until next call to update", async () => { + const error = new Error("intentional error for test"); + spyOn(Resource.prototype, "fetchImage").and.rejectWith(error); skyBox = new SkyBox({ sources: { @@ -376,9 +375,12 @@ describe( scene.skyBox = skyBox; skyBox.update(scene.frameState); - // Flush macro task queue to allow the rejections to be throw in this - // function, otherwise the error ends up happening in `afterAll` - await new Promise((resolve) => window.setTimeout(resolve, 1)); + // Flush macro task queue to allow the rejections to be thrown + await new Promise((resolve) => window.setTimeout(resolve, 0)); + + expect(skyBox._hasError).toBeTrue(); + expect(skyBox._error).toEqual(error); + expect(() => skyBox.update(scene.frameState)).toThrow(error); }); }, "WebGL", From 85b2af0f5ae94d1970a7e5293d9eae71cf516f0d Mon Sep 17 00:00:00 2001 From: Mark Dane Date: Thu, 14 Nov 2024 09:46:04 -0500 Subject: [PATCH 065/175] Use undefined instead of false --- packages/engine/Source/Scene/SkyBox.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/engine/Source/Scene/SkyBox.js b/packages/engine/Source/Scene/SkyBox.js index c2ba96b72d68..39ca3c8b942e 100644 --- a/packages/engine/Source/Scene/SkyBox.js +++ b/packages/engine/Source/Scene/SkyBox.js @@ -82,7 +82,7 @@ function SkyBox(options) { this._attributeLocations = undefined; this._useHdr = undefined; this._hasError = false; - this._error = false; + this._error = undefined; } /** From d385f34702edd1a52b128cb84b2f4a1187f72257 Mon Sep 17 00:00:00 2001 From: Jeshurun Hembd Date: Thu, 14 Nov 2024 10:07:34 -0500 Subject: [PATCH 066/175] PR feedback: simplify types for helper function in Texture --- packages/engine/Source/Renderer/Texture.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/engine/Source/Renderer/Texture.js b/packages/engine/Source/Renderer/Texture.js index a59ca336746f..d65fa422b2a6 100644 --- a/packages/engine/Source/Renderer/Texture.js +++ b/packages/engine/Source/Renderer/Texture.js @@ -390,7 +390,7 @@ function loadBufferSource(texture, source) { * Load texel data from a buffer into part of a texture * * @param {Texture} texture The texture to which texel values will be loaded. - * @param {object} source The source for texel values to be loaded into the texture. + * @param {TypedArray} arrayBufferView The texel values to be loaded into the texture. * @param {number} xOffset The texel x coordinate of the lower left corner of the subregion of the texture to be updated. * @param {number} yOffset The texel y coordinate of the lower left corner of the subregion of the texture to be updated. * @param {number} width The width of the source data, in pixels. @@ -400,7 +400,7 @@ function loadBufferSource(texture, source) { */ function loadPartialBufferSource( texture, - source, + arrayBufferView, xOffset, yOffset, width, @@ -420,7 +420,6 @@ function loadPartialBufferSource( gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false); gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false); - let { arrayBufferView } = source; if (texture.flipY) { arrayBufferView = PixelFormat.flipY( arrayBufferView, From 35fdfd14b8d0fe0e80dce8768fb50032a903be06 Mon Sep 17 00:00:00 2001 From: Jeshurun Hembd Date: Thu, 14 Nov 2024 10:08:07 -0500 Subject: [PATCH 067/175] PR feedback: run Texture specs in both WebGL1 and WebGL2 --- packages/engine/Specs/Renderer/TextureSpec.js | 2577 +++++++++-------- 1 file changed, 1292 insertions(+), 1285 deletions(-) diff --git a/packages/engine/Specs/Renderer/TextureSpec.js b/packages/engine/Specs/Renderer/TextureSpec.js index 609a6a95da47..b9cd1b386199 100644 --- a/packages/engine/Specs/Renderer/TextureSpec.js +++ b/packages/engine/Specs/Renderer/TextureSpec.js @@ -16,1075 +16,1203 @@ import { } from "../../index.js"; import createContext from "../../../../Specs/createContext.js"; +import createWebglVersionHelper from "../createWebglVersionHelper.js"; describe( "Renderer/Texture", function () { - let context; - let greenImage; - let blueImage; - let blueAlphaImage; - let blueOverRedImage; - let blueOverRedFlippedImage; - let red16x16Image; - - let greenKTX2Image; - let greenBasisKTX2Image; - - const fs = - "uniform sampler2D u_texture;" + - "void main() { out_FragColor = texture(u_texture, vec2(0.0)); }"; - const fsLuminanceAlpha = - "uniform sampler2D u_texture;" + - "void main() { out_FragColor = vec4(texture(u_texture, vec2(0.0)).ra, 0.0, 1.0); }"; - let texture; - const uniformMap = { - u_texture: function () { - return texture; - }, - }; - - beforeAll(function () { - context = createContext(); - const promises = []; - promises.push( - Resource.fetchImage("./Data/Images/Green.png").then(function (image) { - greenImage = image; - }), - ); - promises.push( - Resource.fetchImage("./Data/Images/Blue.png").then(function (image) { - blueImage = image; - }), - ); - promises.push( - Resource.fetchImage("./Data/Images/BlueAlpha.png").then( - function (image) { - blueAlphaImage = image; - }, - ), - ); - promises.push( - Resource.fetchImage("./Data/Images/BlueOverRed.png").then( - function (image) { - blueOverRedImage = image; - }, - ), - ); - // Load this image as an ImageBitmap - promises.push( - Resource.fetchImage({ - url: "./Data/Images/BlueOverRed.png", - preferImageBitmap: true, - }).then(function (image) { - blueOverRedFlippedImage = image; - }), - ); - promises.push( - Resource.fetchImage("./Data/Images/Red16x16.png").then( - function (image) { - red16x16Image = image; - }, - ), - ); - - const resource = Resource.createIfNeeded("./Data/Images/Green4x4.ktx2"); - const loadPromise = resource.fetchArrayBuffer(); - promises.push( - loadPromise.then(function (buffer) { - const promise = KTX2Transcoder.transcode(buffer, {}); - return promise.then(function (result) { - greenKTX2Image = result; - }); - }), - ); + createWebglVersionHelper(createTextureSpecs); + + function createTextureSpecs(contextOptions) { + let context; + let greenImage; + let blueImage; + let blueAlphaImage; + let blueOverRedImage; + let blueOverRedFlippedImage; + let red16x16Image; + + let greenKTX2Image; + let greenBasisKTX2Image; + + const fs = + "uniform sampler2D u_texture;" + + "void main() { out_FragColor = texture(u_texture, vec2(0.0)); }"; + const fsLuminanceAlpha = + "uniform sampler2D u_texture;" + + "void main() { out_FragColor = vec4(texture(u_texture, vec2(0.0)).ra, 0.0, 1.0); }"; + let texture; + const uniformMap = { + u_texture: function () { + return texture; + }, + }; - if (context.supportsBasis) { + beforeAll(function () { + context = createContext(contextOptions); + const promises = []; promises.push( - loadKTX2("./Data/Images/Green4x4_ETC1S.ktx2").then(function (image) { - greenBasisKTX2Image = image; + Resource.fetchImage("./Data/Images/Green.png").then(function (image) { + greenImage = image; }), ); - } - - return Promise.all(promises); - }); - - afterAll(function () { - context.destroyForSpecs(); - }); - - let blueImageHeight, blueImageWidth; - beforeEach(function () { - blueImageHeight = blueImage.height; - blueImageWidth = blueImage.width; - }); - - afterEach(function () { - blueImage.height = blueImageHeight; - blueImage.width = blueImageWidth; - texture = texture && texture.destroy(); - }); - - it("has expected default values for pixel format and datatype", function () { - texture = new Texture({ - context: context, - source: blueImage, - }); - - expect(texture.id).toBeDefined(); - expect(texture.pixelFormat).toEqual(PixelFormat.RGBA); - expect(texture.pixelDatatype).toEqual(PixelDatatype.UNSIGNED_BYTE); - }); - - it("can create a texture from the framebuffer", function () { - const command = new ClearCommand({ - color: Color.RED, - }); - command.execute(context); - - texture = Texture.fromFramebuffer({ - context: context, - }); - - const expectedWidth = context.canvas.clientWidth; - const expectedHeight = context.canvas.clientHeight; - expect(texture.width).toEqual(expectedWidth); - expect(texture.height).toEqual(expectedHeight); - expect(texture.sizeInBytes).toEqual( - expectedWidth * - expectedHeight * - PixelFormat.componentsLength(texture.pixelFormat), - ); - - command.color = Color.WHITE; - command.execute(context); - expect(context).toReadPixels([255, 255, 255, 255]); - - expect({ - context: context, - fragmentShader: fs, - uniformMap: uniformMap, - }).contextToRender([255, 0, 0, 255]); - }); - - it("can copy from the framebuffer", function () { - texture = new Texture({ - context: context, - source: blueImage, - pixelFormat: PixelFormat.RGB, - }); - - // Render blue - expect({ - context: context, - fragmentShader: fs, - uniformMap: uniformMap, - }).contextToRender([0, 0, 255, 255]); - - // Clear to red - const command = new ClearCommand({ - color: Color.RED, - }); - command.execute(context); - expect(context).toReadPixels(Color.RED.toBytes()); - - texture.copyFromFramebuffer(); - - const expectedWidth = context.canvas.clientWidth; - const expectedHeight = context.canvas.clientHeight; - expect(texture.width).toEqual(expectedWidth); - expect(texture.height).toEqual(expectedHeight); - expect(texture.sizeInBytes).toEqual( - expectedWidth * - expectedHeight * - PixelFormat.componentsLength(texture.pixelFormat), - ); - - // Clear to white - command.color = Color.WHITE; - command.execute(context); - expect(context).toReadPixels(Color.WHITE.toBytes()); - - // Render red - expect({ - context: context, - fragmentShader: fs, - uniformMap: uniformMap, - }).contextToRender([255, 0, 0, 255]); - }); - - it("draws the expected texture color", function () { - texture = new Texture({ - context: context, - source: blueImage, - pixelFormat: PixelFormat.RGBA, - }); - - expect({ - context: context, - fragmentShader: fs, - uniformMap: uniformMap, - }).contextToRender([0, 0, 255, 255]); - }); - - it("cannot flip texture when using ImageBitmap", function () { - const topColor = new Color(0.0, 0.0, 1.0, 1.0); - let bottomColor = new Color(1.0, 0.0, 0.0, 1.0); - - return Resource.supportsImageBitmapOptions().then( - function (supportsImageBitmapOptions) { - if (supportsImageBitmapOptions) { - // When imageBitmapOptions is supported, flipY on texture upload is ignored. - bottomColor = topColor; - } - - texture = new Texture({ - context: context, - source: blueOverRedFlippedImage, - pixelFormat: PixelFormat.RGBA, - flipY: false, - }); + promises.push( + Resource.fetchImage("./Data/Images/Blue.png").then(function (image) { + blueImage = image; + }), + ); + promises.push( + Resource.fetchImage("./Data/Images/BlueAlpha.png").then( + function (image) { + blueAlphaImage = image; + }, + ), + ); + promises.push( + Resource.fetchImage("./Data/Images/BlueOverRed.png").then( + function (image) { + blueOverRedImage = image; + }, + ), + ); + // Load this image as an ImageBitmap + promises.push( + Resource.fetchImage({ + url: "./Data/Images/BlueOverRed.png", + preferImageBitmap: true, + }).then(function (image) { + blueOverRedFlippedImage = image; + }), + ); + promises.push( + Resource.fetchImage("./Data/Images/Red16x16.png").then( + function (image) { + red16x16Image = image; + }, + ), + ); - expect({ - context: context, - fragmentShader: fs, - uniformMap: uniformMap, - }).contextToRender(topColor.toBytes()); + const resource = Resource.createIfNeeded("./Data/Images/Green4x4.ktx2"); + const loadPromise = resource.fetchArrayBuffer(); + promises.push( + loadPromise.then(function (buffer) { + const promise = KTX2Transcoder.transcode(buffer, {}); + return promise.then(function (result) { + greenKTX2Image = result; + }); + }), + ); - // Flip the texture. - texture = new Texture({ - context: context, - source: blueOverRedFlippedImage, - pixelFormat: PixelFormat.RGBA, - flipY: true, - }); + if (context.supportsBasis) { + promises.push( + loadKTX2("./Data/Images/Green4x4_ETC1S.ktx2").then( + function (image) { + greenBasisKTX2Image = image; + }, + ), + ); + } + + return Promise.all(promises); + }); - expect({ - context: context, - fragmentShader: fs, - uniformMap: uniformMap, - }).contextToRender(bottomColor.toBytes()); - }, - ); - }); + afterAll(function () { + context.destroyForSpecs(); + }); - it("draws the expected floating-point texture color", function () { - if (!context.floatingPointTexture) { - return; - } + let blueImageHeight, blueImageWidth; + beforeEach(function () { + blueImageHeight = blueImage.height; + blueImageWidth = blueImage.width; + }); - const color = new Color(0.2, 0.4, 0.6, 1.0); - const floats = new Float32Array([ - color.red, - color.green, - color.blue, - color.alpha, - ]); - - texture = new Texture({ - context: context, - pixelFormat: PixelFormat.RGBA, - pixelDatatype: PixelDatatype.FLOAT, - source: { - width: 1, - height: 1, - arrayBufferView: floats, - }, + afterEach(function () { + blueImage.height = blueImageHeight; + blueImage.width = blueImageWidth; + texture = texture && texture.destroy(); }); - expect(texture.sizeInBytes).toEqual(16); + it("has expected default values for pixel format and datatype", function () { + texture = new Texture({ + context: context, + source: blueImage, + }); - expect({ - context: context, - fragmentShader: fs, - uniformMap: uniformMap, - }).contextToRender(color.toBytes()); - }); + expect(texture.id).toBeDefined(); + expect(texture.pixelFormat).toEqual(PixelFormat.RGBA); + expect(texture.pixelDatatype).toEqual(PixelDatatype.UNSIGNED_BYTE); + }); - it("draws the expected floating-point texture color with linear filtering", function () { - if (!context.floatingPointTexture) { - return; - } + it("can create a texture from the framebuffer", function () { + const command = new ClearCommand({ + color: Color.RED, + }); + command.execute(context); - const color0 = new Color(0.2, 0.4, 0.6, 1.0); - const color1 = new Color(0.1, 0.3, 0.5, 1.0); - const floats = new Float32Array([ - color0.red, - color0.green, - color0.blue, - color0.alpha, - color1.red, - color1.green, - color1.blue, - color1.alpha, - ]); - - texture = new Texture({ - context: context, - pixelFormat: PixelFormat.RGBA, - pixelDatatype: PixelDatatype.FLOAT, - source: { - width: 2, - height: 1, - arrayBufferView: floats, - }, - sampler: new Sampler({ - wrapS: TextureWrap.CLAMP_TO_EDGE, - wrapT: TextureWrap.CLAMP_TO_EDGE, - minificationFilter: TextureMinificationFilter.LINEAR, - magnificationFilter: TextureMagnificationFilter.LINEAR, - }), - }); + texture = Texture.fromFramebuffer({ + context: context, + }); - expect(texture.sizeInBytes).toEqual(32); + const expectedWidth = context.canvas.clientWidth; + const expectedHeight = context.canvas.clientHeight; + expect(texture.width).toEqual(expectedWidth); + expect(texture.height).toEqual(expectedHeight); + expect(texture.sizeInBytes).toEqual( + expectedWidth * + expectedHeight * + PixelFormat.componentsLength(texture.pixelFormat), + ); - const fs = - "uniform sampler2D u_texture;" + - "void main() { out_FragColor = texture(u_texture, vec2(0.5, 0.0)); }"; + command.color = Color.WHITE; + command.execute(context); + expect(context).toReadPixels([255, 255, 255, 255]); - if (!context.textureFloatLinear) { expect({ context: context, fragmentShader: fs, uniformMap: uniformMap, - epsilon: 1, - }).contextToRender(color1.toBytes()); - } else { - Color.multiplyByScalar(color0, 1.0 - 0.5, color0); - Color.multiplyByScalar(color1, 0.5, color1); - Color.add(color0, color1, color1); + }).contextToRender([255, 0, 0, 255]); + }); + + it("can copy from the framebuffer", function () { + texture = new Texture({ + context: context, + source: blueImage, + pixelFormat: PixelFormat.RGB, + }); + + // Render blue expect({ context: context, fragmentShader: fs, uniformMap: uniformMap, - }).contextToRender(color1.toBytes()); - } - }); + }).contextToRender([0, 0, 255, 255]); - it("draws the expected half floating-point texture color", function () { - if (!context.halfFloatingPointTexture) { - return; - } + // Clear to red + const command = new ClearCommand({ + color: Color.RED, + }); + command.execute(context); + expect(context).toReadPixels(Color.RED.toBytes()); + + texture.copyFromFramebuffer(); + + const expectedWidth = context.canvas.clientWidth; + const expectedHeight = context.canvas.clientHeight; + expect(texture.width).toEqual(expectedWidth); + expect(texture.height).toEqual(expectedHeight); + expect(texture.sizeInBytes).toEqual( + expectedWidth * + expectedHeight * + PixelFormat.componentsLength(texture.pixelFormat), + ); - const color = new Color(0.2, 0.4, 0.6, 1.0); - const floats = new Uint16Array([12902, 13926, 14541, 15360]); + // Clear to white + command.color = Color.WHITE; + command.execute(context); + expect(context).toReadPixels(Color.WHITE.toBytes()); - texture = new Texture({ - context: context, - pixelFormat: PixelFormat.RGBA, - pixelDatatype: PixelDatatype.HALF_FLOAT, - source: { - width: 1, - height: 1, - arrayBufferView: floats, - }, - flipY: false, + // Render red + expect({ + context: context, + fragmentShader: fs, + uniformMap: uniformMap, + }).contextToRender([255, 0, 0, 255]); }); - expect(texture.sizeInBytes).toEqual(8); - - expect({ - context: context, - fragmentShader: fs, - uniformMap: uniformMap, - }).contextToRender(color.toBytes()); - }); + it("draws the expected texture color", function () { + texture = new Texture({ + context: context, + source: blueImage, + pixelFormat: PixelFormat.RGBA, + }); - it("draws the expected half floating-point texture color with linear filtering", function () { - if (!context.halfFloatingPointTexture) { - return; - } + expect({ + context: context, + fragmentShader: fs, + uniformMap: uniformMap, + }).contextToRender([0, 0, 255, 255]); + }); - const color0 = new Color(0.2, 0.4, 0.6, 1.0); - const color1 = new Color(0.1, 0.3, 0.5, 1.0); - const floats = new Uint16Array([ - 12902, 13926, 14541, 15360, 11878, 13517, 14336, 15360, - ]); - - texture = new Texture({ - context: context, - pixelFormat: PixelFormat.RGBA, - pixelDatatype: PixelDatatype.HALF_FLOAT, - source: { - width: 2, - height: 1, - arrayBufferView: floats, - }, - flipY: false, + it("cannot flip texture when using ImageBitmap", function () { + const topColor = new Color(0.0, 0.0, 1.0, 1.0); + let bottomColor = new Color(1.0, 0.0, 0.0, 1.0); + + return Resource.supportsImageBitmapOptions().then( + function (supportsImageBitmapOptions) { + if (supportsImageBitmapOptions) { + // When imageBitmapOptions is supported, flipY on texture upload is ignored. + bottomColor = topColor; + } + + texture = new Texture({ + context: context, + source: blueOverRedFlippedImage, + pixelFormat: PixelFormat.RGBA, + flipY: false, + }); + + expect({ + context: context, + fragmentShader: fs, + uniformMap: uniformMap, + }).contextToRender(topColor.toBytes()); + + // Flip the texture. + texture = new Texture({ + context: context, + source: blueOverRedFlippedImage, + pixelFormat: PixelFormat.RGBA, + flipY: true, + }); + + expect({ + context: context, + fragmentShader: fs, + uniformMap: uniformMap, + }).contextToRender(bottomColor.toBytes()); + }, + ); }); - expect(texture.sizeInBytes).toEqual(16); + it("draws the expected floating-point texture color", function () { + if (!context.floatingPointTexture) { + return; + } - const fs = - "uniform sampler2D u_texture;" + - "void main() { out_FragColor = texture(u_texture, vec2(0.5, 0.0)); }"; + const color = new Color(0.2, 0.4, 0.6, 1.0); + const floats = new Float32Array([ + color.red, + color.green, + color.blue, + color.alpha, + ]); - if (!context.textureHalfFloatLinear) { - expect({ + texture = new Texture({ context: context, - fragmentShader: fs, - uniformMap: uniformMap, - epsilon: 1, - }).contextToRender(color1.toBytes()); - } else { - Color.multiplyByScalar(color0, 1.0 - 0.5, color0); - Color.multiplyByScalar(color1, 0.5, color1); - Color.add(color0, color1, color1); + pixelFormat: PixelFormat.RGBA, + pixelDatatype: PixelDatatype.FLOAT, + source: { + width: 1, + height: 1, + arrayBufferView: floats, + }, + }); + + expect(texture.sizeInBytes).toEqual(16); + expect({ context: context, fragmentShader: fs, uniformMap: uniformMap, - }).contextToRender(color1.toBytes()); - } - }); + }).contextToRender(color.toBytes()); + }); - it("draws the expected Basis compressed texture color", function () { - if (!context.supportsBasis) { - return; - } + it("draws the expected floating-point texture color with linear filtering", function () { + if (!context.floatingPointTexture) { + return; + } + + const color0 = new Color(0.2, 0.4, 0.6, 1.0); + const color1 = new Color(0.1, 0.3, 0.5, 1.0); + const floats = new Float32Array([ + color0.red, + color0.green, + color0.blue, + color0.alpha, + color1.red, + color1.green, + color1.blue, + color1.alpha, + ]); - texture = new Texture({ - context: context, - pixelFormat: greenBasisKTX2Image.internalFormat, - source: { - width: greenBasisKTX2Image.width, - height: greenBasisKTX2Image.height, - arrayBufferView: greenBasisKTX2Image.bufferView, - }, - }); + texture = new Texture({ + context: context, + pixelFormat: PixelFormat.RGBA, + pixelDatatype: PixelDatatype.FLOAT, + source: { + width: 2, + height: 1, + arrayBufferView: floats, + }, + sampler: new Sampler({ + wrapS: TextureWrap.CLAMP_TO_EDGE, + wrapT: TextureWrap.CLAMP_TO_EDGE, + minificationFilter: TextureMinificationFilter.LINEAR, + magnificationFilter: TextureMagnificationFilter.LINEAR, + }), + }); - expect(texture.sizeInBytes).toBe(8); + expect(texture.sizeInBytes).toEqual(32); - expect({ - context: context, - fragmentShader: fs, - uniformMap: uniformMap, - }).contextToRenderAndCall(function (color) { - return expect(color).toEqualEpsilon([2, 255, 2, 255], 2); - }); - }); + const fs = + "uniform sampler2D u_texture;" + + "void main() { out_FragColor = texture(u_texture, vec2(0.5, 0.0)); }"; - it("draws the expected KTX2 uncompressed texture color", function () { - texture = new Texture({ - context: context, - pixelFormat: greenKTX2Image.internalFormat, - source: { - width: greenKTX2Image.width, - height: greenKTX2Image.height, - arrayBufferView: greenKTX2Image.bufferView, - }, + if (!context.textureFloatLinear) { + expect({ + context: context, + fragmentShader: fs, + uniformMap: uniformMap, + epsilon: 1, + }).contextToRender(color1.toBytes()); + } else { + Color.multiplyByScalar(color0, 1.0 - 0.5, color0); + Color.multiplyByScalar(color1, 0.5, color1); + Color.add(color0, color1, color1); + expect({ + context: context, + fragmentShader: fs, + uniformMap: uniformMap, + }).contextToRender(color1.toBytes()); + } }); - expect(texture.sizeInBytes).toBe(48); + it("draws the expected half floating-point texture color", function () { + if (!context.halfFloatingPointTexture) { + return; + } - expect({ - context: context, - fragmentShader: fs, - uniformMap: uniformMap, - }).contextToRenderAndCall(function (color) { - return expect(color).toEqualEpsilon([0, 255, 24, 255], 2); - }); - }); + const color = new Color(0.2, 0.4, 0.6, 1.0); + const floats = new Uint16Array([12902, 13926, 14541, 15360]); - it("renders with premultiplied alpha", function () { - const cxt = createContext({ - webgl: { - alpha: true, - }, - }); - const texture = new Texture({ - context: cxt, - source: blueAlphaImage, - pixelFormat: PixelFormat.RGBA, - preMultiplyAlpha: true, - }); - const uniformMap = { - u_texture: function () { - return texture; - }, - }; + texture = new Texture({ + context: context, + pixelFormat: PixelFormat.RGBA, + pixelDatatype: PixelDatatype.HALF_FLOAT, + source: { + width: 1, + height: 1, + arrayBufferView: floats, + }, + flipY: false, + }); - expect(texture.preMultiplyAlpha).toEqual(true); - expect({ - context: cxt, - fragmentShader: fs, - uniformMap: uniformMap, - epsilon: 1, - }).contextToRender([0, 0, 127, 127]); - - texture.destroy(); - cxt.destroyForSpecs(); - }); - - it("draws textured blue and red points", function () { - texture = new Texture({ - context: context, - source: blueOverRedImage, - pixelFormat: PixelFormat.RGBA, - }); - - let fragmentShaderSource = ""; - fragmentShaderSource += "uniform sampler2D u_texture;"; - fragmentShaderSource += "uniform mediump vec2 u_txCoords;"; - fragmentShaderSource += - "void main() { out_FragColor = texture(u_texture, u_txCoords); }"; - - let txCoords; - const um = { - u_texture: function () { - return texture; - }, - u_txCoords: function () { - return txCoords; - }, - }; + expect(texture.sizeInBytes).toEqual(8); - // Blue on top - txCoords = new Cartesian2(0.5, 0.75); - expect({ - context: context, - fragmentShader: fragmentShaderSource, - uniformMap: um, - }).contextToRender([0, 0, 255, 255]); - - // Red on bottom - txCoords = new Cartesian2(0.5, 0.25); - expect({ - context: context, - fragmentShader: fragmentShaderSource, - uniformMap: um, - }).contextToRender([255, 0, 0, 255]); - }); - - it("draws the expected luminance texture color", function () { - const color = new Color(0.6, 0.6, 0.6, 1.0); - const arrayBufferView = new Uint8Array([153]); - - texture = new Texture({ - context: context, - pixelFormat: PixelFormat.LUMINANCE, - pixelDatatype: PixelDatatype.UNSIGNED_BYTE, - source: { - width: 1, - height: 1, - arrayBufferView: arrayBufferView, - }, - flipY: false, + expect({ + context: context, + fragmentShader: fs, + uniformMap: uniformMap, + }).contextToRender(color.toBytes()); }); - expect({ - context: context, - fragmentShader: fs, - uniformMap: uniformMap, - }).contextToRender(color.toBytes()); - }); + it("draws the expected half floating-point texture color with linear filtering", function () { + if (!context.halfFloatingPointTexture) { + return; + } - it("draws the expected luminance alpha texture color", function () { - const color = new Color(0.6, 0.8, 0.0, 1.0); - const arrayBufferView = new Uint8Array([153, 204]); + const color0 = new Color(0.2, 0.4, 0.6, 1.0); + const color1 = new Color(0.1, 0.3, 0.5, 1.0); + const floats = new Uint16Array([ + 12902, 13926, 14541, 15360, 11878, 13517, 14336, 15360, + ]); - texture = new Texture({ - context: context, - pixelFormat: PixelFormat.LUMINANCE_ALPHA, - pixelDatatype: PixelDatatype.UNSIGNED_BYTE, - source: { - width: 1, - height: 1, - arrayBufferView: arrayBufferView, - }, - flipY: false, - }); + texture = new Texture({ + context: context, + pixelFormat: PixelFormat.RGBA, + pixelDatatype: PixelDatatype.HALF_FLOAT, + source: { + width: 2, + height: 1, + arrayBufferView: floats, + }, + flipY: false, + }); - expect({ - context: context, - fragmentShader: fsLuminanceAlpha, - uniformMap: uniformMap, - }).contextToRender(color.toBytes()); - }); + expect(texture.sizeInBytes).toEqual(16); - it("can be created from a typed array", function () { - const bytes = new Uint8Array([0, 255, 0, 255]); + const fs = + "uniform sampler2D u_texture;" + + "void main() { out_FragColor = texture(u_texture, vec2(0.5, 0.0)); }"; - texture = new Texture({ - context: context, - pixelFormat: PixelFormat.RGBA, - pixelDatatype: PixelDatatype.UNSIGNED_BYTE, - source: { - width: 1, - height: 1, - arrayBufferView: bytes, - }, + if (!context.textureHalfFloatLinear) { + expect({ + context: context, + fragmentShader: fs, + uniformMap: uniformMap, + epsilon: 1, + }).contextToRender(color1.toBytes()); + } else { + Color.multiplyByScalar(color0, 1.0 - 0.5, color0); + Color.multiplyByScalar(color1, 0.5, color1); + Color.add(color0, color1, color1); + expect({ + context: context, + fragmentShader: fs, + uniformMap: uniformMap, + }).contextToRender(color1.toBytes()); + } }); - expect(texture.width).toEqual(1); - expect(texture.height).toEqual(1); - expect(texture.sizeInBytes).toEqual(4); + it("draws the expected Basis compressed texture color", function () { + if (!context.supportsBasis) { + return; + } - expect({ - context: context, - fragmentShader: fs, - uniformMap: uniformMap, - }).contextToRender([0, 255, 0, 255]); - }); + texture = new Texture({ + context: context, + pixelFormat: greenBasisKTX2Image.internalFormat, + source: { + width: greenBasisKTX2Image.width, + height: greenBasisKTX2Image.height, + arrayBufferView: greenBasisKTX2Image.bufferView, + }, + }); - it("can copy from a typed array", function () { - texture = new Texture({ - context: context, - pixelFormat: PixelFormat.RGBA, - pixelDatatype: PixelDatatype.UNSIGNED_BYTE, - width: 1, - height: 1, - }); + expect(texture.sizeInBytes).toBe(8); - const bytes = new Uint8Array(Color.NAVY.toBytes()); - texture.copyFrom({ - source: { - width: 1, - height: 1, - arrayBufferView: bytes, - }, + expect({ + context: context, + fragmentShader: fs, + uniformMap: uniformMap, + }).contextToRenderAndCall(function (color) { + return expect(color).toEqualEpsilon([2, 255, 2, 255], 2); + }); }); - expect(texture.width).toEqual(1); - expect(texture.height).toEqual(1); - expect(texture.sizeInBytes).toEqual(4); - - expect({ - context: context, - fragmentShader: fs, - uniformMap: uniformMap, - }).contextToRender(Color.NAVY.toBytes()); - }); + it("draws the expected KTX2 uncompressed texture color", function () { + texture = new Texture({ + context: context, + pixelFormat: greenKTX2Image.internalFormat, + source: { + width: greenKTX2Image.width, + height: greenKTX2Image.height, + arrayBufferView: greenKTX2Image.bufferView, + }, + }); - it("can copy from a DOM element", function () { - texture = new Texture({ - context: context, - pixelFormat: PixelFormat.RGB, - pixelDatatype: PixelDatatype.UNSIGNED_BYTE, - width: blueImage.width, - height: blueImage.height, - }); + expect(texture.sizeInBytes).toBe(48); - texture.copyFrom({ - source: blueImage, + expect({ + context: context, + fragmentShader: fs, + uniformMap: uniformMap, + }).contextToRenderAndCall(function (color) { + return expect(color).toEqualEpsilon([0, 255, 24, 255], 2); + }); }); - expect({ - context: context, - fragmentShader: fs, - uniformMap: uniformMap, - epsilon: 1, - }).contextToRender([0, 0, 255, 255]); - }); + it("renders with premultiplied alpha", function () { + const alphaContextOptions = Object.assign( + { + webgl: { + alpha: true, + }, + }, + contextOptions, + ); + const alphaContext = createContext(alphaContextOptions); + const texture = new Texture({ + context: alphaContext, + source: blueAlphaImage, + pixelFormat: PixelFormat.RGBA, + preMultiplyAlpha: true, + }); + const uniformMap = { + u_texture: function () { + return texture; + }, + }; - it("can copy from a DOM element when display dimensions are 0", function () { - blueImage.height = 0; - blueImage.width = 0; + expect(texture.preMultiplyAlpha).toEqual(true); + expect({ + context: alphaContext, + fragmentShader: fs, + uniformMap: uniformMap, + epsilon: 1, + }).contextToRender([0, 0, 127, 127]); - texture = new Texture({ - context: context, - pixelFormat: PixelFormat.RGB, - pixelDatatype: PixelDatatype.UNSIGNED_BYTE, - source: blueImage, + texture.destroy(); + alphaContext.destroyForSpecs(); }); - texture.copyFrom({ - source: blueImage, - }); + it("draws textured blue and red points", function () { + texture = new Texture({ + context: context, + source: blueOverRedImage, + pixelFormat: PixelFormat.RGBA, + }); - expect({ - context: context, - fragmentShader: fs, - uniformMap: uniformMap, - epsilon: 1, - }).contextToRender([0, 0, 255, 255]); - }); + let fragmentShaderSource = ""; + fragmentShaderSource += "uniform sampler2D u_texture;"; + fragmentShaderSource += "uniform mediump vec2 u_txCoords;"; + fragmentShaderSource += + "void main() { out_FragColor = texture(u_texture, u_txCoords); }"; - it("can replace a subset of a texture", function () { - texture = new Texture({ - context: context, - source: blueOverRedImage, - pixelFormat: PixelFormat.RGBA, - }); + let txCoords; + const um = { + u_texture: function () { + return texture; + }, + u_txCoords: function () { + return txCoords; + }, + }; - let fragmentShaderSource = ""; - fragmentShaderSource += "uniform sampler2D u_texture;"; - fragmentShaderSource += "uniform mediump vec2 u_txCoords;"; - fragmentShaderSource += - "void main() { out_FragColor = texture(u_texture, u_txCoords); }"; + // Blue on top + txCoords = new Cartesian2(0.5, 0.75); + expect({ + context: context, + fragmentShader: fragmentShaderSource, + uniformMap: um, + }).contextToRender([0, 0, 255, 255]); - let txCoords; - const um = { - u_texture: function () { - return texture; - }, - u_txCoords: function () { - return txCoords; - }, - }; + // Red on bottom + txCoords = new Cartesian2(0.5, 0.25); + expect({ + context: context, + fragmentShader: fragmentShaderSource, + uniformMap: um, + }).contextToRender([255, 0, 0, 255]); + }); - // Blue on top - txCoords = new Cartesian2(0.5, 0.75); - expect({ - context: context, - fragmentShader: fragmentShaderSource, - uniformMap: um, - }).contextToRender([0, 0, 255, 255]); - - // Red on bottom - txCoords = new Cartesian2(0.5, 0.25); - expect({ - context: context, - fragmentShader: fragmentShaderSource, - uniformMap: um, - }).contextToRender([255, 0, 0, 255]); - - // After copy... - texture.copyFrom({ - source: greenImage, - xOffset: 0, - yOffset: 1, - }); - - // Now green on top - txCoords = new Cartesian2(0.5, 0.75); - expect({ - context: context, - fragmentShader: fragmentShaderSource, - uniformMap: um, - }).contextToRender(Color.LIME.toBytes()); - - // Still red on bottom - txCoords = new Cartesian2(0.5, 0.25); - expect({ - context: context, - fragmentShader: fragmentShaderSource, - uniformMap: um, - }).contextToRender([255, 0, 0, 255]); - }); - - it("can generate mipmaps", function () { - texture = new Texture({ - context: context, - source: red16x16Image, - pixelFormat: PixelFormat.RGBA, - sampler: new Sampler({ - minificationFilter: TextureMinificationFilter.NEAREST_MIPMAP_LINEAR, - }), - }); - texture.generateMipmap(); - expect(texture.sizeInBytes).toEqualEpsilon( - (16 * 16 + 8 * 8 + 4 * 4 + 2 * 2 + 1) * 4, - 1, - ); - - expect({ - context: context, - fragmentShader: fs, - uniformMap: uniformMap, - }).contextToRender([255, 0, 0, 255]); - }); - - it("can set a sampler property", function () { - texture = new Texture({ - context: context, - source: blueImage, - pixelFormat: PixelFormat.RGBA, - }); - - const sampler = new Sampler({ - wrapS: TextureWrap.REPEAT, - wrapT: TextureWrap.MIRRORED_REPEAT, - minificationFilter: TextureMinificationFilter.NEAREST, - magnificationFilter: TextureMagnificationFilter.NEAREST, - maximumAnisotropy: 2.0, - }); - texture.sampler = sampler; - - const s = texture.sampler; - expect(s.wrapS).toEqual(sampler.wrapS); - expect(s.wrapT).toEqual(sampler.wrapT); - expect(s.minificationFilter).toEqual(sampler.minificationFilter); - expect(s.magnificationFilter).toEqual(sampler.magnificationFilter); - expect(s.maximumAnisotropy).toEqual(2.0); - }); - - it("can set sampler at construction", function () { - texture = new Texture({ - context: context, - source: blueImage, - pixelFormat: PixelFormat.RGBA, - sampler: new Sampler({ - wrapS: TextureWrap.REPEAT, - wrapT: TextureWrap.MIRRORED_REPEAT, - minificationFilter: TextureMinificationFilter.NEAREST, - magnificationFilter: TextureMagnificationFilter.NEAREST, - maximumAnisotropy: 2.0, - }), - }); - - const s = texture.sampler; - expect(s.wrapS).toEqual(TextureWrap.REPEAT); - expect(s.wrapT).toEqual(TextureWrap.MIRRORED_REPEAT); - expect(s.minificationFilter).toEqual(TextureMinificationFilter.NEAREST); - expect(s.magnificationFilter).toEqual(TextureMagnificationFilter.NEAREST); - expect(s.maximumAnisotropy).toEqual(2.0); - }); - - it("can get width and height", function () { - texture = new Texture({ - context: context, - source: blueOverRedImage, - pixelFormat: PixelFormat.RGBA, - }); - - expect(texture.width).toEqual(1); - expect(texture.height).toEqual(2); - }); - - it("can get whether Y is flipped", function () { - texture = new Texture({ - context: context, - source: blueOverRedImage, - pixelFormat: PixelFormat.RGBA, - flipY: true, - }); - - expect(texture.flipY).toEqual(true); - }); - - it("can get the dimensions of a texture", function () { - texture = new Texture({ - context: context, - width: 64, - height: 16, - }); - - expect(texture.dimensions).toEqual(new Cartesian2(64, 16)); - }); - - function expectTextureByteSize( - width, - height, - pixelFormat, - pixelDatatype, - expectedSize, - ) { - texture = new Texture({ - context: context, - width: width, - height: height, - pixelFormat: pixelFormat, - pixelDatatype: pixelDatatype, - }); - expect(texture.sizeInBytes).toBe(expectedSize); - texture = texture && texture.destroy(); - } + it("draws the expected luminance texture color", function () { + const color = new Color(0.6, 0.6, 0.6, 1.0); + const arrayBufferView = new Uint8Array([153]); - it("can get the size in bytes of a texture", function () { - // Depth textures - if (context.depthTexture) { - expectTextureByteSize( - 16, - 16, - PixelFormat.DEPTH_COMPONENT, - PixelDatatype.UNSIGNED_SHORT, - 256 * 2, - ); - expectTextureByteSize( - 16, - 16, - PixelFormat.DEPTH_COMPONENT, - PixelDatatype.UNSIGNED_INT, - 256 * 4, - ); - expectTextureByteSize( - 16, - 16, - PixelFormat.DEPTH_STENCIL, - PixelDatatype.UNSIGNED_INT_24_8, - 256 * 4, - ); - } + texture = new Texture({ + context: context, + pixelFormat: PixelFormat.LUMINANCE, + pixelDatatype: PixelDatatype.UNSIGNED_BYTE, + source: { + width: 1, + height: 1, + arrayBufferView: arrayBufferView, + }, + flipY: false, + }); + + expect({ + context: context, + fragmentShader: fs, + uniformMap: uniformMap, + }).contextToRender(color.toBytes()); + }); + + it("draws the expected luminance alpha texture color", function () { + const color = new Color(0.6, 0.8, 0.0, 1.0); + const arrayBufferView = new Uint8Array([153, 204]); - // Uncompressed formats - expectTextureByteSize( - 16, - 16, - PixelFormat.ALPHA, - PixelDatatype.UNSIGNED_BYTE, - 256, - ); - expectTextureByteSize( - 16, - 16, - PixelFormat.RGB, - PixelDatatype.UNSIGNED_BYTE, - 256 * 3, - ); - expectTextureByteSize( - 16, - 16, - PixelFormat.RGBA, - PixelDatatype.UNSIGNED_BYTE, - 256 * 4, - ); - expectTextureByteSize( - 16, - 16, - PixelFormat.LUMINANCE, - PixelDatatype.UNSIGNED_BYTE, - 256, - ); - expectTextureByteSize( - 16, - 16, - PixelFormat.LUMINANCE_ALPHA, - PixelDatatype.UNSIGNED_BYTE, - 256 * 2, - ); - }); - - it("can be destroyed", function () { - const t = new Texture({ - context: context, - source: blueImage, - pixelFormat: PixelFormat.RGBA, - }); - - expect(t.isDestroyed()).toEqual(false); - t.destroy(); - expect(t.isDestroyed()).toEqual(true); - }); - - it("throws when creating a texture without a options", function () { - expect(function () { - texture = new Texture(); - }).toThrowDeveloperError(); - }); - - it("throws when creating a texture without a source", function () { - expect(function () { texture = new Texture({ context: context, + pixelFormat: PixelFormat.LUMINANCE_ALPHA, + pixelDatatype: PixelDatatype.UNSIGNED_BYTE, + source: { + width: 1, + height: 1, + arrayBufferView: arrayBufferView, + }, + flipY: false, }); - }).toThrowDeveloperError(); - }); - it("throws when creating a texture with width and no height", function () { - expect(function () { + expect({ + context: context, + fragmentShader: fsLuminanceAlpha, + uniformMap: uniformMap, + }).contextToRender(color.toBytes()); + }); + + it("can be created from a typed array", function () { + const bytes = new Uint8Array([0, 255, 0, 255]); + texture = new Texture({ context: context, - width: 16, + pixelFormat: PixelFormat.RGBA, + pixelDatatype: PixelDatatype.UNSIGNED_BYTE, + source: { + width: 1, + height: 1, + arrayBufferView: bytes, + }, }); - }).toThrowDeveloperError(); - }); - it("throws when creating a texture with height and no width", function () { - expect(function () { + expect(texture.width).toEqual(1); + expect(texture.height).toEqual(1); + expect(texture.sizeInBytes).toEqual(4); + + expect({ + context: context, + fragmentShader: fs, + uniformMap: uniformMap, + }).contextToRender([0, 255, 0, 255]); + }); + + it("can copy from a typed array", function () { texture = new Texture({ context: context, - height: 16, + pixelFormat: PixelFormat.RGBA, + pixelDatatype: PixelDatatype.UNSIGNED_BYTE, + width: 1, + height: 1, }); - }).toThrowDeveloperError(); - }); - it("throws when creating a texture with zero width", function () { - expect(function () { + const bytes = new Uint8Array(Color.NAVY.toBytes()); + texture.copyFrom({ + source: { + width: 1, + height: 1, + arrayBufferView: bytes, + }, + }); + + expect(texture.width).toEqual(1); + expect(texture.height).toEqual(1); + expect(texture.sizeInBytes).toEqual(4); + + expect({ + context: context, + fragmentShader: fs, + uniformMap: uniformMap, + }).contextToRender(Color.NAVY.toBytes()); + }); + + it("can copy from a DOM element", function () { texture = new Texture({ context: context, - width: 0, - height: 16, + pixelFormat: PixelFormat.RGB, + pixelDatatype: PixelDatatype.UNSIGNED_BYTE, + width: blueImage.width, + height: blueImage.height, }); - }).toThrowDeveloperError(); - }); - it("throws when creating a texture with width larger than the maximum texture size", function () { - expect(function () { + texture.copyFrom({ + source: blueImage, + }); + + expect({ + context: context, + fragmentShader: fs, + uniformMap: uniformMap, + epsilon: 1, + }).contextToRender([0, 0, 255, 255]); + }); + + it("can copy from a DOM element when display dimensions are 0", function () { + blueImage.height = 0; + blueImage.width = 0; + texture = new Texture({ context: context, - width: ContextLimits.maximumTextureSize + 1, - height: 16, + pixelFormat: PixelFormat.RGB, + pixelDatatype: PixelDatatype.UNSIGNED_BYTE, + source: blueImage, }); - }).toThrowDeveloperError(); - }); - it("throws when creating a texture with zero height", function () { - expect(function () { + texture.copyFrom({ + source: blueImage, + }); + + expect({ + context: context, + fragmentShader: fs, + uniformMap: uniformMap, + epsilon: 1, + }).contextToRender([0, 0, 255, 255]); + }); + + it("can replace a subset of a texture", function () { texture = new Texture({ context: context, - width: 16, - height: 0, + source: blueOverRedImage, + pixelFormat: PixelFormat.RGBA, }); - }).toThrowDeveloperError(); - }); - it("throws when creating a texture with height larger than the maximum texture size", function () { - expect(function () { + let fragmentShaderSource = ""; + fragmentShaderSource += "uniform sampler2D u_texture;"; + fragmentShaderSource += "uniform mediump vec2 u_txCoords;"; + fragmentShaderSource += + "void main() { out_FragColor = texture(u_texture, u_txCoords); }"; + + let txCoords; + const um = { + u_texture: function () { + return texture; + }, + u_txCoords: function () { + return txCoords; + }, + }; + + // Blue on top + txCoords = new Cartesian2(0.5, 0.75); + expect({ + context: context, + fragmentShader: fragmentShaderSource, + uniformMap: um, + }).contextToRender([0, 0, 255, 255]); + + // Red on bottom + txCoords = new Cartesian2(0.5, 0.25); + expect({ + context: context, + fragmentShader: fragmentShaderSource, + uniformMap: um, + }).contextToRender([255, 0, 0, 255]); + + // After copy... + texture.copyFrom({ + source: greenImage, + xOffset: 0, + yOffset: 1, + }); + + // Now green on top + txCoords = new Cartesian2(0.5, 0.75); + expect({ + context: context, + fragmentShader: fragmentShaderSource, + uniformMap: um, + }).contextToRender(Color.LIME.toBytes()); + + // Still red on bottom + txCoords = new Cartesian2(0.5, 0.25); + expect({ + context: context, + fragmentShader: fragmentShaderSource, + uniformMap: um, + }).contextToRender([255, 0, 0, 255]); + }); + + it("can generate mipmaps", function () { texture = new Texture({ context: context, - width: 16, - height: ContextLimits.maximumTextureSize + 1, + source: red16x16Image, + pixelFormat: PixelFormat.RGBA, + sampler: new Sampler({ + minificationFilter: TextureMinificationFilter.NEAREST_MIPMAP_LINEAR, + }), }); - }).toThrowDeveloperError(); - }); + texture.generateMipmap(); + expect(texture.sizeInBytes).toEqualEpsilon( + (16 * 16 + 8 * 8 + 4 * 4 + 2 * 2 + 1) * 4, + 1, + ); + + expect({ + context: context, + fragmentShader: fs, + uniformMap: uniformMap, + }).contextToRender([255, 0, 0, 255]); + }); - it("throws when creating a texture with an invalid pixel format", function () { - expect(function () { + it("can set a sampler property", function () { texture = new Texture({ context: context, source: blueImage, - pixelFormat: "invalid PixelFormat", + pixelFormat: PixelFormat.RGBA, + }); + + const sampler = new Sampler({ + wrapS: TextureWrap.REPEAT, + wrapT: TextureWrap.MIRRORED_REPEAT, + minificationFilter: TextureMinificationFilter.NEAREST, + magnificationFilter: TextureMagnificationFilter.NEAREST, + maximumAnisotropy: 2.0, }); - }).toThrowDeveloperError(); - }); + texture.sampler = sampler; + + const s = texture.sampler; + expect(s.wrapS).toEqual(sampler.wrapS); + expect(s.wrapT).toEqual(sampler.wrapT); + expect(s.minificationFilter).toEqual(sampler.minificationFilter); + expect(s.magnificationFilter).toEqual(sampler.magnificationFilter); + expect(s.maximumAnisotropy).toEqual(2.0); + }); - it("throws when creating a texture with an invalid pixel datatype", function () { - expect(function () { + it("can set sampler at construction", function () { texture = new Texture({ context: context, source: blueImage, pixelFormat: PixelFormat.RGBA, - pixelDatatype: "invalid pixelDatatype", + sampler: new Sampler({ + wrapS: TextureWrap.REPEAT, + wrapT: TextureWrap.MIRRORED_REPEAT, + minificationFilter: TextureMinificationFilter.NEAREST, + magnificationFilter: TextureMagnificationFilter.NEAREST, + maximumAnisotropy: 2.0, + }), }); - }).toThrowDeveloperError(); - }); - it("throws when creating if pixelFormat is DEPTH_COMPONENT and pixelDatatype is not UNSIGNED_SHORT or UNSIGNED_INT", function () { - expect(function () { - texture = new Texture({ - context: context, - pixelFormat: PixelFormat.DEPTH_COMPONENT, - pixelDatatype: PixelDatatype.UNSIGNED_BYTE, - }); - }).toThrowDeveloperError(); - }); + const s = texture.sampler; + expect(s.wrapS).toEqual(TextureWrap.REPEAT); + expect(s.wrapT).toEqual(TextureWrap.MIRRORED_REPEAT); + expect(s.minificationFilter).toEqual(TextureMinificationFilter.NEAREST); + expect(s.magnificationFilter).toEqual( + TextureMagnificationFilter.NEAREST, + ); + expect(s.maximumAnisotropy).toEqual(2.0); + }); + + it("can get width and height", function () { + texture = new Texture({ + context: context, + source: blueOverRedImage, + pixelFormat: PixelFormat.RGBA, + }); + + expect(texture.width).toEqual(1); + expect(texture.height).toEqual(2); + }); + + it("can get whether Y is flipped", function () { + texture = new Texture({ + context: context, + source: blueOverRedImage, + pixelFormat: PixelFormat.RGBA, + flipY: true, + }); + + expect(texture.flipY).toEqual(true); + }); + + it("can get the dimensions of a texture", function () { + texture = new Texture({ + context: context, + width: 64, + height: 16, + }); + + expect(texture.dimensions).toEqual(new Cartesian2(64, 16)); + }); + + function expectTextureByteSize( + width, + height, + pixelFormat, + pixelDatatype, + expectedSize, + ) { + texture = new Texture({ + context: context, + width: width, + height: height, + pixelFormat: pixelFormat, + pixelDatatype: pixelDatatype, + }); + expect(texture.sizeInBytes).toBe(expectedSize); + texture = texture && texture.destroy(); + } + + it("can get the size in bytes of a texture", function () { + // Depth textures + if (context.depthTexture) { + expectTextureByteSize( + 16, + 16, + PixelFormat.DEPTH_COMPONENT, + PixelDatatype.UNSIGNED_SHORT, + 256 * 2, + ); + expectTextureByteSize( + 16, + 16, + PixelFormat.DEPTH_COMPONENT, + PixelDatatype.UNSIGNED_INT, + 256 * 4, + ); + expectTextureByteSize( + 16, + 16, + PixelFormat.DEPTH_STENCIL, + PixelDatatype.UNSIGNED_INT_24_8, + 256 * 4, + ); + } + + // Uncompressed formats + expectTextureByteSize( + 16, + 16, + PixelFormat.ALPHA, + PixelDatatype.UNSIGNED_BYTE, + 256, + ); + expectTextureByteSize( + 16, + 16, + PixelFormat.RGB, + PixelDatatype.UNSIGNED_BYTE, + 256 * 3, + ); + expectTextureByteSize( + 16, + 16, + PixelFormat.RGBA, + PixelDatatype.UNSIGNED_BYTE, + 256 * 4, + ); + expectTextureByteSize( + 16, + 16, + PixelFormat.LUMINANCE, + PixelDatatype.UNSIGNED_BYTE, + 256, + ); + expectTextureByteSize( + 16, + 16, + PixelFormat.LUMINANCE_ALPHA, + PixelDatatype.UNSIGNED_BYTE, + 256 * 2, + ); + }); + + it("can be destroyed", function () { + const t = new Texture({ + context: context, + source: blueImage, + pixelFormat: PixelFormat.RGBA, + }); + + expect(t.isDestroyed()).toEqual(false); + t.destroy(); + expect(t.isDestroyed()).toEqual(true); + }); + + it("throws when creating a texture without a options", function () { + expect(function () { + texture = new Texture(); + }).toThrowDeveloperError(); + }); + + it("throws when creating a texture without a source", function () { + expect(function () { + texture = new Texture({ + context: context, + }); + }).toThrowDeveloperError(); + }); + + it("throws when creating a texture with width and no height", function () { + expect(function () { + texture = new Texture({ + context: context, + width: 16, + }); + }).toThrowDeveloperError(); + }); + + it("throws when creating a texture with height and no width", function () { + expect(function () { + texture = new Texture({ + context: context, + height: 16, + }); + }).toThrowDeveloperError(); + }); + + it("throws when creating a texture with zero width", function () { + expect(function () { + texture = new Texture({ + context: context, + width: 0, + height: 16, + }); + }).toThrowDeveloperError(); + }); + + it("throws when creating a texture with width larger than the maximum texture size", function () { + expect(function () { + texture = new Texture({ + context: context, + width: ContextLimits.maximumTextureSize + 1, + height: 16, + }); + }).toThrowDeveloperError(); + }); + + it("throws when creating a texture with zero height", function () { + expect(function () { + texture = new Texture({ + context: context, + width: 16, + height: 0, + }); + }).toThrowDeveloperError(); + }); + + it("throws when creating a texture with height larger than the maximum texture size", function () { + expect(function () { + texture = new Texture({ + context: context, + width: 16, + height: ContextLimits.maximumTextureSize + 1, + }); + }).toThrowDeveloperError(); + }); + + it("throws when creating a texture with an invalid pixel format", function () { + expect(function () { + texture = new Texture({ + context: context, + source: blueImage, + pixelFormat: "invalid PixelFormat", + }); + }).toThrowDeveloperError(); + }); + + it("throws when creating a texture with an invalid pixel datatype", function () { + expect(function () { + texture = new Texture({ + context: context, + source: blueImage, + pixelFormat: PixelFormat.RGBA, + pixelDatatype: "invalid pixelDatatype", + }); + }).toThrowDeveloperError(); + }); + + it("throws when creating if pixelFormat is DEPTH_COMPONENT and pixelDatatype is not UNSIGNED_SHORT or UNSIGNED_INT", function () { + expect(function () { + texture = new Texture({ + context: context, + pixelFormat: PixelFormat.DEPTH_COMPONENT, + pixelDatatype: PixelDatatype.UNSIGNED_BYTE, + }); + }).toThrowDeveloperError(); + }); + + it("throws when creating if pixelFormat is DEPTH_STENCIL and pixelDatatype is not UNSIGNED_INT_24_8", function () { + expect(function () { + texture = new Texture({ + context: context, + pixelFormat: PixelFormat.DEPTH_STENCIL, + pixelDatatype: PixelDatatype.UNSIGNED_BYTE, + }); + }).toThrowDeveloperError(); + }); + + it("throws when creating if pixelFormat is DEPTH_COMPONENT or DEPTH_STENCIL, and source is provided", function () { + expect(function () { + texture = new Texture({ + context: context, + source: blueImage, + pixelFormat: PixelFormat.DEPTH_COMPONENT, + pixelDatatype: PixelDatatype.UNSIGNED_SHORT, + }); + }).toThrowDeveloperError(); + }); + + it("throws when creating if pixelFormat is DEPTH_COMPONENT or DEPTH_STENCIL, and WEBGL_depth_texture is not supported", function () { + if (!context.depthTexture) { + expect(function () { + texture = new Texture({ + context: context, + width: 1, + height: 1, + pixelFormat: PixelFormat.DEPTH_COMPONENT, + pixelDatatype: PixelDatatype.UNSIGNED_SHORT, + }); + }).toThrowDeveloperError(); + } + }); + + it("throws when creating if pixelDatatype is FLOAT, and OES_texture_float is not supported", function () { + if (!context.floatingPointTexture) { + expect(function () { + texture = new Texture({ + context: context, + width: 1, + height: 1, + pixelFormat: PixelFormat.RGBA, + pixelDatatype: PixelDatatype.FLOAT, + }); + }).toThrowDeveloperError(); + } + }); + + it("throws when creating if pixelDatatype = HALF_FLOAT, and OES_texture_half_float is not supported", function () { + if (!context.halfFloatingPointTexture) { + expect(function () { + texture = new Texture({ + context: context, + width: 1, + height: 1, + pixelFormat: PixelDatatype.RGBA, + pixelDatatype: PixelDatatype.HALF_FLOAT, + }); + }).toThrowDeveloperError(); + } + }); + + it("throws when creating compressed texture and the array buffer source is undefined", function () { + expect(function () { + texture = new Texture({ + context: context, + width: 4, + height: 4, + pixelFormat: PixelFormat.RGBA_DXT3, + }); + }).toThrowDeveloperError(); + }); + + it("throws when creating from the framebuffer with an invalid pixel format", function () { + expect(function () { + texture = Texture.fromFramebuffer({ + context: context, + pixelFormat: "invalid PixelFormat", + }); + }).toThrowDeveloperError(); + }); + + it("throws when creating from the framebuffer if PixelFormat is DEPTH_COMPONENT or DEPTH_STENCIL", function () { + expect(function () { + texture = Texture.fromFramebuffer({ + context: context, + pixelFormat: PixelFormat.DEPTH_COMPONENT, + }); + }).toThrowDeveloperError(); + }); + + it("throws when creating from the framebuffer with a negative framebufferXOffset", function () { + expect(function () { + texture = Texture.fromFramebuffer({ + context: context, + pixelFormat: PixelFormat.RGB, + framebufferXOffset: -1, + }); + }).toThrowDeveloperError(); + }); - it("throws when creating if pixelFormat is DEPTH_STENCIL and pixelDatatype is not UNSIGNED_INT_24_8", function () { - expect(function () { - texture = new Texture({ - context: context, - pixelFormat: PixelFormat.DEPTH_STENCIL, - pixelDatatype: PixelDatatype.UNSIGNED_BYTE, - }); - }).toThrowDeveloperError(); - }); + it("throws when creating from the framebuffer with a negative framebufferYOffset", function () { + expect(function () { + texture = Texture.fromFramebuffer({ + context: context, + pixelFormat: PixelFormat.RGB, + framebufferXOffset: 0, + framebufferYOffset: -1, + }); + }).toThrowDeveloperError(); + }); - it("throws when creating if pixelFormat is DEPTH_COMPONENT or DEPTH_STENCIL, and source is provided", function () { - expect(function () { - texture = new Texture({ - context: context, - source: blueImage, - pixelFormat: PixelFormat.DEPTH_COMPONENT, - pixelDatatype: PixelDatatype.UNSIGNED_SHORT, - }); - }).toThrowDeveloperError(); - }); + it("throws when creating from the framebuffer with a width greater than the canvas clientWidth", function () { + expect(function () { + texture = Texture.fromFramebuffer({ + context: context, + pixelFormat: PixelFormat.RGB, + framebufferXOffset: 0, + framebufferYOffset: 0, + width: context.canvas.clientWidth + 1, + }); + }).toThrowDeveloperError(); + }); - it("throws when creating if pixelFormat is DEPTH_COMPONENT or DEPTH_STENCIL, and WEBGL_depth_texture is not supported", function () { - if (!context.depthTexture) { + it("throws when creating from the framebuffer with a height greater than the canvas clientHeight", function () { expect(function () { + texture = Texture.fromFramebuffer({ + context: context, + pixelFormat: PixelFormat.RGB, + framebufferXOffset: 0, + framebufferYOffset: 0, + width: 1, + height: context.canvas.clientHeight + 1, + }); + }).toThrowDeveloperError(); + }); + + it("throws when copying to a texture from the framebuffer with a DEPTH_COMPONENT or DEPTH_STENCIL pixel format", function () { + if (context.depthTexture) { texture = new Texture({ context: context, width: 1, @@ -1092,13 +1220,33 @@ describe( pixelFormat: PixelFormat.DEPTH_COMPONENT, pixelDatatype: PixelDatatype.UNSIGNED_SHORT, }); - }).toThrowDeveloperError(); - } - }); - it("throws when creating if pixelDatatype is FLOAT, and OES_texture_float is not supported", function () { - if (!context.floatingPointTexture) { - expect(function () { + expect(function () { + texture.copyFromFramebuffer(); + }).toThrowDeveloperError(); + } + }); + + it("throws when copying to a texture from the framebuffer with a compressed pixel format", function () { + if (context.supportsBasis) { + texture = new Texture({ + context: context, + width: greenBasisKTX2Image.width, + height: greenBasisKTX2Image.height, + pixelFormat: greenBasisKTX2Image.internalFormat, + source: { + arrayBufferView: greenBasisKTX2Image.bufferView, + }, + }); + + expect(function () { + texture.copyFromFramebuffer(); + }).toThrowDeveloperError(); + } + }); + + it("throws when copying to a texture from the framebuffer with a FLOAT pixel data type", function () { + if (context.floatingPointTexture) { texture = new Texture({ context: context, width: 1, @@ -1106,454 +1254,313 @@ describe( pixelFormat: PixelFormat.RGBA, pixelDatatype: PixelDatatype.FLOAT, }); - }).toThrowDeveloperError(); - } - }); - it("throws when creating if pixelDatatype = HALF_FLOAT, and OES_texture_half_float is not supported", function () { - if (!context.halfFloatingPointTexture) { - expect(function () { + expect(function () { + texture.copyFromFramebuffer(); + }).toThrowDeveloperError(); + } + }); + + it("throws when copying to a texture from the framebuffer with a HALF_FLOAT pixel data type", function () { + if (context.halfFloatingPointTexture) { texture = new Texture({ context: context, width: 1, height: 1, - pixelFormat: PixelDatatype.RGBA, + pixelFormat: PixelFormat.RGBA, pixelDatatype: PixelDatatype.HALF_FLOAT, }); - }).toThrowDeveloperError(); - } - }); - - it("throws when creating compressed texture and the array buffer source is undefined", function () { - expect(function () { - texture = new Texture({ - context: context, - width: 4, - height: 4, - pixelFormat: PixelFormat.RGBA_DXT3, - }); - }).toThrowDeveloperError(); - }); - - it("throws when creating from the framebuffer with an invalid pixel format", function () { - expect(function () { - texture = Texture.fromFramebuffer({ - context: context, - pixelFormat: "invalid PixelFormat", - }); - }).toThrowDeveloperError(); - }); - it("throws when creating from the framebuffer if PixelFormat is DEPTH_COMPONENT or DEPTH_STENCIL", function () { - expect(function () { - texture = Texture.fromFramebuffer({ - context: context, - pixelFormat: PixelFormat.DEPTH_COMPONENT, - }); - }).toThrowDeveloperError(); - }); + expect(function () { + texture.copyFromFramebuffer(); + }).toThrowDeveloperError(); + } + }); - it("throws when creating from the framebuffer with a negative framebufferXOffset", function () { - expect(function () { - texture = Texture.fromFramebuffer({ + it("throws when copying from the framebuffer with a negative xOffset", function () { + texture = new Texture({ context: context, - pixelFormat: PixelFormat.RGB, - framebufferXOffset: -1, + source: blueImage, }); - }).toThrowDeveloperError(); - }); - it("throws when creating from the framebuffer with a negative framebufferYOffset", function () { - expect(function () { - texture = Texture.fromFramebuffer({ - context: context, - pixelFormat: PixelFormat.RGB, - framebufferXOffset: 0, - framebufferYOffset: -1, - }); - }).toThrowDeveloperError(); - }); + expect(function () { + texture.copyFromFramebuffer(-1); + }).toThrowDeveloperError(); + }); - it("throws when creating from the framebuffer with a width greater than the canvas clientWidth", function () { - expect(function () { - texture = Texture.fromFramebuffer({ + it("throws when copying from the framebuffer with a negative yOffset", function () { + texture = new Texture({ context: context, - pixelFormat: PixelFormat.RGB, - framebufferXOffset: 0, - framebufferYOffset: 0, - width: context.canvas.clientWidth + 1, + source: blueImage, }); - }).toThrowDeveloperError(); - }); - it("throws when creating from the framebuffer with a height greater than the canvas clientHeight", function () { - expect(function () { - texture = Texture.fromFramebuffer({ - context: context, - pixelFormat: PixelFormat.RGB, - framebufferXOffset: 0, - framebufferYOffset: 0, - width: 1, - height: context.canvas.clientHeight + 1, - }); - }).toThrowDeveloperError(); - }); + expect(function () { + texture.copyFromFramebuffer(0, -1); + }).toThrowDeveloperError(); + }); - it("throws when copying to a texture from the framebuffer with a DEPTH_COMPONENT or DEPTH_STENCIL pixel format", function () { - if (context.depthTexture) { + it("throws when copying from the framebuffer with a negative framebufferXOffset", function () { texture = new Texture({ context: context, - width: 1, - height: 1, - pixelFormat: PixelFormat.DEPTH_COMPONENT, - pixelDatatype: PixelDatatype.UNSIGNED_SHORT, + source: blueImage, }); expect(function () { - texture.copyFromFramebuffer(); + texture.copyFromFramebuffer(0, 0, -1); }).toThrowDeveloperError(); - } - }); + }); - it("throws when copying to a texture from the framebuffer with a compressed pixel format", function () { - if (context.supportsBasis) { + it("throws when copying from the framebuffer with a negative framebufferYOffset", function () { texture = new Texture({ context: context, - width: greenBasisKTX2Image.width, - height: greenBasisKTX2Image.height, - pixelFormat: greenBasisKTX2Image.internalFormat, - source: { - arrayBufferView: greenBasisKTX2Image.bufferView, - }, + source: blueImage, }); expect(function () { - texture.copyFromFramebuffer(); + texture.copyFromFramebuffer(0, 0, 0, -1); }).toThrowDeveloperError(); - } - }); + }); - it("throws when copying to a texture from the framebuffer with a FLOAT pixel data type", function () { - if (context.floatingPointTexture) { + it("throws when copying from the framebuffer with a larger width", function () { texture = new Texture({ context: context, - width: 1, - height: 1, - pixelFormat: PixelFormat.RGBA, - pixelDatatype: PixelDatatype.FLOAT, + source: blueImage, }); expect(function () { - texture.copyFromFramebuffer(); + texture.copyFromFramebuffer(0, 0, 0, 0, texture.width + 1); }).toThrowDeveloperError(); - } - }); + }); - it("throws when copying to a texture from the framebuffer with a HALF_FLOAT pixel data type", function () { - if (context.halfFloatingPointTexture) { + it("throws when copying from the framebuffer with a larger height", function () { texture = new Texture({ context: context, - width: 1, - height: 1, - pixelFormat: PixelFormat.RGBA, - pixelDatatype: PixelDatatype.HALF_FLOAT, + source: blueImage, }); expect(function () { - texture.copyFromFramebuffer(); + texture.copyFromFramebuffer(0, 0, 0, 0, 0, texture.height + 1); }).toThrowDeveloperError(); - } - }); - - it("throws when copying from the framebuffer with a negative xOffset", function () { - texture = new Texture({ - context: context, - source: blueImage, - }); - - expect(function () { - texture.copyFromFramebuffer(-1); - }).toThrowDeveloperError(); - }); - - it("throws when copying from the framebuffer with a negative yOffset", function () { - texture = new Texture({ - context: context, - source: blueImage, - }); - - expect(function () { - texture.copyFromFramebuffer(0, -1); - }).toThrowDeveloperError(); - }); - - it("throws when copying from the framebuffer with a negative framebufferXOffset", function () { - texture = new Texture({ - context: context, - source: blueImage, - }); - - expect(function () { - texture.copyFromFramebuffer(0, 0, -1); - }).toThrowDeveloperError(); - }); - - it("throws when copying from the framebuffer with a negative framebufferYOffset", function () { - texture = new Texture({ - context: context, - source: blueImage, }); - expect(function () { - texture.copyFromFramebuffer(0, 0, 0, -1); - }).toThrowDeveloperError(); - }); + it("throws when copying to a texture with a DEPTH_COMPONENT or DEPTH_STENCIL pixel format", function () { + if (context.depthTexture) { + texture = new Texture({ + context: context, + width: 1, + height: 1, + pixelFormat: PixelFormat.DEPTH_COMPONENT, + pixelDatatype: PixelDatatype.UNSIGNED_SHORT, + }); - it("throws when copying from the framebuffer with a larger width", function () { - texture = new Texture({ - context: context, - source: blueImage, + expect(function () { + texture.copyFrom({ + source: { + arrayBufferView: new Uint16Array([0]), + width: 1, + height: 1, + }, + }); + }).toThrowDeveloperError(); + } }); - expect(function () { - texture.copyFromFramebuffer(0, 0, 0, 0, texture.width + 1); - }).toThrowDeveloperError(); - }); + it("throws when copyFrom is not given any options", function () { + texture = new Texture({ + context: context, + source: blueImage, + }); - it("throws when copying from the framebuffer with a larger height", function () { - texture = new Texture({ - context: context, - source: blueImage, + expect(function () { + texture.copyFrom(); + }).toThrowDeveloperError(); }); - expect(function () { - texture.copyFromFramebuffer(0, 0, 0, 0, 0, texture.height + 1); - }).toThrowDeveloperError(); - }); - - it("throws when copying to a texture with a DEPTH_COMPONENT or DEPTH_STENCIL pixel format", function () { - if (context.depthTexture) { + it("throws when copyFrom is not given a source", function () { texture = new Texture({ context: context, - width: 1, - height: 1, - pixelFormat: PixelFormat.DEPTH_COMPONENT, - pixelDatatype: PixelDatatype.UNSIGNED_SHORT, + source: blueImage, }); expect(function () { texture.copyFrom({ - source: { - arrayBufferView: new Uint16Array([0]), - width: 1, - height: 1, - }, + xOffset: 0, + yOffset: 2, }); }).toThrowDeveloperError(); - } - }); - - it("throws when copyFrom is not given any options", function () { - texture = new Texture({ - context: context, - source: blueImage, - }); - - expect(function () { - texture.copyFrom(); - }).toThrowDeveloperError(); - }); - - it("throws when copyFrom is not given a source", function () { - texture = new Texture({ - context: context, - source: blueImage, - }); - - expect(function () { - texture.copyFrom({ - xOffset: 0, - yOffset: 2, - }); - }).toThrowDeveloperError(); - }); - - it("throws when copyFrom is given a negative xOffset", function () { - texture = new Texture({ - context: context, - source: blueImage, }); - expect(function () { - texture.copyFrom({ + it("throws when copyFrom is given a negative xOffset", function () { + texture = new Texture({ + context: context, source: blueImage, - xOffset: -1, }); - }).toThrowDeveloperError(); - }); - it("throws when copyFrom is given a negative yOffset", function () { - texture = new Texture({ - context: context, - source: blueImage, + expect(function () { + texture.copyFrom({ + source: blueImage, + xOffset: -1, + }); + }).toThrowDeveloperError(); }); - expect(function () { - texture.copyFrom({ + it("throws when copyFrom is given a negative yOffset", function () { + texture = new Texture({ + context: context, source: blueImage, - xOffset: 0, - yOffset: -1, }); - }).toThrowDeveloperError(); - }); - it("throws when copyFrom is given a source with larger width", function () { - texture = new Texture({ - context: context, - source: blueImage, - }); - const image = new Image(); - image.width = blueImage.width + 1; - - expect(function () { - texture.copyFrom({ - source: image, - }); - }).toThrowDeveloperError(); - }); - - it("throws when copyFrom is given a source with larger height", function () { - texture = new Texture({ - context: context, - source: blueImage, + expect(function () { + texture.copyFrom({ + source: blueImage, + xOffset: 0, + yOffset: -1, + }); + }).toThrowDeveloperError(); }); - const image = new Image(); - image.height = blueImage.height + 1; - - expect(function () { - texture.copyFrom({ - source: image, - }); - }).toThrowDeveloperError(); - }); - it("throws when copyFrom is given a source with a compressed pixel format", function () { - if (context.supportsBasis) { + it("throws when copyFrom is given a source with larger width", function () { texture = new Texture({ context: context, - width: greenBasisKTX2Image.width, - height: greenBasisKTX2Image.height, - pixelFormat: greenBasisKTX2Image.internalFormat, - source: { - arrayBufferView: greenBasisKTX2Image.bufferView, - }, + source: blueImage, }); - const image = new Image(); + image.width = blueImage.width + 1; + expect(function () { texture.copyFrom({ source: image, }); }).toThrowDeveloperError(); - } - }); + }); - it("throws when generating mipmaps with a DEPTH_COMPONENT or DEPTH_STENCIL pixel format", function () { - if (context.depthTexture) { + it("throws when copyFrom is given a source with larger height", function () { texture = new Texture({ context: context, - width: 1, - height: 1, - pixelFormat: PixelFormat.DEPTH_COMPONENT, - pixelDatatype: PixelDatatype.UNSIGNED_SHORT, + source: blueImage, }); + const image = new Image(); + image.height = blueImage.height + 1; expect(function () { - texture.generateMipmap(); + texture.copyFrom({ + source: image, + }); }).toThrowDeveloperError(); - } - }); + }); - it("throws when generating mipmaps with a compressed pixel format", function () { - if (context.supportsBasis) { - texture = new Texture({ - context: context, - width: greenBasisKTX2Image.width, - height: greenBasisKTX2Image.height, - pixelFormat: greenBasisKTX2Image.internalFormat, - source: { - arrayBufferView: greenBasisKTX2Image.bufferView, - }, - }); + it("throws when copyFrom is given a source with a compressed pixel format", function () { + if (context.supportsBasis) { + texture = new Texture({ + context: context, + width: greenBasisKTX2Image.width, + height: greenBasisKTX2Image.height, + pixelFormat: greenBasisKTX2Image.internalFormat, + source: { + arrayBufferView: greenBasisKTX2Image.bufferView, + }, + }); - expect(function () { - texture.generateMipmap(); - }).toThrowDeveloperError(); - } - }); + const image = new Image(); + expect(function () { + texture.copyFrom({ + source: image, + }); + }).toThrowDeveloperError(); + } + }); - describe("WebGL1", function () { - let webgl1Context; + it("throws when generating mipmaps with a DEPTH_COMPONENT or DEPTH_STENCIL pixel format", function () { + if (context.depthTexture) { + texture = new Texture({ + context: context, + width: 1, + height: 1, + pixelFormat: PixelFormat.DEPTH_COMPONENT, + pixelDatatype: PixelDatatype.UNSIGNED_SHORT, + }); - beforeAll(() => { - webgl1Context = createContext({ - requestWebgl1: true, - }); + expect(function () { + texture.generateMipmap(); + }).toThrowDeveloperError(); + } }); - afterAll(() => { - webgl1Context.destroyForSpecs(); + it("throws when generating mipmaps with a compressed pixel format", function () { + if (context.supportsBasis) { + texture = new Texture({ + context: context, + width: greenBasisKTX2Image.width, + height: greenBasisKTX2Image.height, + pixelFormat: greenBasisKTX2Image.internalFormat, + source: { + arrayBufferView: greenBasisKTX2Image.bufferView, + }, + }); + + expect(function () { + texture.generateMipmap(); + }).toThrowDeveloperError(); + } }); - it("throws when generating mipmaps with a non-power of two width", function () { - texture = new Texture({ - context: webgl1Context, - width: 3, - height: 2, + describe("WebGL1", function () { + it("throws when generating mipmaps with a non-power of two width", function () { + if (context.webgl2) { + return; + } + texture = new Texture({ + context: context, + width: 3, + height: 2, + }); + + expect(function () { + texture.generateMipmap(); + }).toThrowDeveloperError(); }); - expect(function () { - texture.generateMipmap(); - }).toThrowDeveloperError(); + it("throws when generating mipmaps with a non-power of two height", function () { + if (context.webgl2) { + return; + } + texture = new Texture({ + context: context, + width: 2, + height: 3, + }); + + expect(function () { + texture.generateMipmap(); + }).toThrowDeveloperError(); + }); }); - it("throws when generating mipmaps with a non-power of two height", function () { + it("throws when generating mipmaps with an invalid hint", function () { texture = new Texture({ - context: webgl1Context, - width: 2, - height: 3, + context: context, + source: blueImage, }); expect(function () { - texture.generateMipmap(); + texture.generateMipmap("invalid hint"); }).toThrowDeveloperError(); }); - }); - it("throws when generating mipmaps with an invalid hint", function () { - texture = new Texture({ - context: context, - source: blueImage, - }); + it("throws when destroy is called after destroying", function () { + const t = new Texture({ + context: context, + source: blueImage, + pixelFormat: PixelFormat.RGBA, + }); - expect(function () { - texture.generateMipmap("invalid hint"); - }).toThrowDeveloperError(); - }); + t.destroy(); - it("throws when destroy is called after destroying", function () { - const t = new Texture({ - context: context, - source: blueImage, - pixelFormat: PixelFormat.RGBA, + expect(function () { + t.destroy(); + }).toThrowDeveloperError(); }); - - t.destroy(); - - expect(function () { - t.destroy(); - }).toThrowDeveloperError(); - }); + } }, "WebGL", ); From 4c2575caa98327107c11606d48a3790bdc03297e Mon Sep 17 00:00:00 2001 From: Mark Dane Date: Thu, 14 Nov 2024 10:14:19 -0500 Subject: [PATCH 068/175] Use pollPromise instead of trying flush async queue --- packages/engine/Specs/Scene/SkyBoxSpec.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/engine/Specs/Scene/SkyBoxSpec.js b/packages/engine/Specs/Scene/SkyBoxSpec.js index 96ebab2a79d0..ec9749d2eb33 100644 --- a/packages/engine/Specs/Scene/SkyBoxSpec.js +++ b/packages/engine/Specs/Scene/SkyBoxSpec.js @@ -1,6 +1,7 @@ -import { Resource, SceneMode, SkyBox } from "../../index.js"; +import { defined, Resource, SceneMode, SkyBox } from "../../index.js"; import createScene from "../../../../Specs/createScene.js"; +import pollToPromise from "../../../../Specs/pollToPromise.js"; describe( "Scene/SkyBox", @@ -375,8 +376,7 @@ describe( scene.skyBox = skyBox; skyBox.update(scene.frameState); - // Flush macro task queue to allow the rejections to be thrown - await new Promise((resolve) => window.setTimeout(resolve, 0)); + await pollToPromise(() => defined(skyBox._cubeMap) || skyBox._hasError); expect(skyBox._hasError).toBeTrue(); expect(skyBox._error).toEqual(error); From f04ee84a9bdbb7e96a097e2c7ee71e43ae599513 Mon Sep 17 00:00:00 2001 From: Mark Dane <48362463+angrycat9000@users.noreply.github.com> Date: Thu, 14 Nov 2024 10:15:47 -0500 Subject: [PATCH 069/175] Update CHANGES.md Co-authored-by: Gabby Getz --- CHANGES.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 21757139473e..aaaaef20aecd 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,9 +2,17 @@ ### 1.124 - 2024-12-01 +#### @cesium/engine + +##### Additions :tada: + +- Added `GoogleGeocoderService` for standalone usage of Google geocoder. [#12299](https://github.com/CesiumGS/cesium/pull/12299) + +#### @cesium/widgets + ##### Additions :tada: -- Ability to choose between Bing and Google geocoders. Adds `GoogleGeocoderService` for standalone usage of Google geocoder. Updates `Viewer` constructor to also accept `IonGeocoderProvider` [#12299](https://github.com/CesiumGS/cesium/pull/12299) +- Added the ability to choose between Bing and Google geocoders. Updated `Viewer` constructor to also accept `IonGeocoderProvider` [#12299](https://github.com/CesiumGS/cesium/pull/12299) ### 1.123.1 - 2024-11-07 From 4e91c995ab980092e0cd62ca6435c8d3594b942a Mon Sep 17 00:00:00 2001 From: Mark Dane Date: Thu, 14 Nov 2024 10:19:59 -0500 Subject: [PATCH 070/175] Minor PR feedback --- CHANGES.md | 2 +- packages/engine/Source/Core/GoogleGeocoderService.js | 7 +++++-- packages/engine/Source/Core/IonGeocoderService.js | 2 ++ packages/widgets/Source/Viewer/Viewer.js | 2 +- 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index aaaaef20aecd..3c05703f0a0e 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -6,7 +6,7 @@ ##### Additions :tada: -- Added `GoogleGeocoderService` for standalone usage of Google geocoder. [#12299](https://github.com/CesiumGS/cesium/pull/12299) +- Added `GoogleGeocoderService` for standalone usage of Google geocoder. [#12299](https://github.com/CesiumGS/cesium/pull/12299) #### @cesium/widgets diff --git a/packages/engine/Source/Core/GoogleGeocoderService.js b/packages/engine/Source/Core/GoogleGeocoderService.js index b8eab86dba4e..67326e496e1c 100644 --- a/packages/engine/Source/Core/GoogleGeocoderService.js +++ b/packages/engine/Source/Core/GoogleGeocoderService.js @@ -5,6 +5,7 @@ import Rectangle from "./Rectangle.js"; import Resource from "./Resource.js"; import defined from "./defined.js"; import DeveloperError from "./DeveloperError.js"; +import RuntimeError from "./RuntimeError.js"; const API_URL = "https://maps.googleapis.com/maps/api/geocode/json"; const CREDIT_HTML = `Google`; @@ -52,11 +53,13 @@ Object.defineProperties(GoogleGeocoderService.prototype, { }); /** + * Get a list of possible locations that match a search string. + * * @function * * @param {string} query The query to be sent to the geocoder service * @returns {Promise} - * @throws {Error} If the services returns a status other than OK or ZERO_RESULTS + * @throws {RuntimeError} If the services returns a status other than OK or ZERO_RESULTS */ GoogleGeocoderService.prototype.geocode = async function (query) { // See API documentation at https://developers.google.com/maps/documentation/geocoding/requests-geocoding @@ -78,7 +81,7 @@ GoogleGeocoderService.prototype.geocode = async function (query) { } if (response.status !== "OK") { - throw new Error( + throw new RuntimeError( `GoogleGeocoderService got a bad response ${response.status}: ${response.error_message}`, ); } diff --git a/packages/engine/Source/Core/IonGeocoderService.js b/packages/engine/Source/Core/IonGeocoderService.js index eb5d5a973ae0..008ef8b0680b 100644 --- a/packages/engine/Source/Core/IonGeocoderService.js +++ b/packages/engine/Source/Core/IonGeocoderService.js @@ -106,8 +106,10 @@ Object.defineProperties(IonGeocoderService.prototype, { }, }, /** + * The geocoding service that Cesium ion API server should use to fulfill geocding requests. * @memberof IonGeocoderService.prototype * @type {IonGeocodeProvider} + * @default IonGeocodeProvider.DEFAULT */ geocodeProvider: { get: function () { diff --git a/packages/widgets/Source/Viewer/Viewer.js b/packages/widgets/Source/Viewer/Viewer.js index f7503b537d15..44e32292f0ab 100644 --- a/packages/widgets/Source/Viewer/Viewer.js +++ b/packages/widgets/Source/Viewer/Viewer.js @@ -281,7 +281,7 @@ function enableVRUI(viewer, enabled) { * @property {boolean} [baseLayerPicker=true] If set to false, the BaseLayerPicker widget will not be created. * @property {boolean} [fullscreenButton=true] If set to false, the FullscreenButton widget will not be created. * @property {boolean} [vrButton=false] If set to true, the VRButton widget will be created. - * @property {boolean|IonGeocodeProvider|GeocoderService[]} [geocoder=IonGeocodeProvider.DEFAULT] If set to false, the Geocoder widget will not be created. + * @property {boolean|IonGeocodeProvider|GeocoderService[]} [geocoder=IonGeocodeProvider.DEFAULT] The geocoding service or services to use when searching with the Geocoder widget. If set to false, the Geocoder widget will not be created. * @property {boolean} [homeButton=true] If set to false, the HomeButton widget will not be created. * @property {boolean} [infoBox=true] If set to false, the InfoBox widget will not be created. * @property {boolean} [sceneModePicker=true] If set to false, the SceneModePicker widget will not be created. From a78112116b3b32976424c1244dda7491811301e4 Mon Sep 17 00:00:00 2001 From: Mark Dane Date: Thu, 14 Nov 2024 10:32:37 -0500 Subject: [PATCH 071/175] Rename IonGeocodeProvder -> IonGeocodeProviderType --- .../3D Tiles Vertical Exaggeration.html | 2 +- Apps/Sandcastle/gallery/AEC Clipping.html | 2 +- .../gallery/Bing Maps Labels Only.html | 2 +- .../gallery/Clamp Entities to Ground.html | 2 +- Apps/Sandcastle/gallery/Globe Interior.html | 2 +- ...alistic 3D Tiles with Building Insert.html | 2 +- .../Google Photorealistic 3D Tiles.html | 2 +- .../gallery/Imagery Color To Alpha.html | 2 +- .../gallery/Imagery Layers Manipulation.html | 2 +- ...eProvider.js => IonGeocodeProviderType.js} | 4 ++-- .../engine/Source/Core/IonGeocoderService.js | 24 +++++++++---------- .../createGooglePhotorealistic3DTileset.js | 4 ++-- .../Specs/Core/IonGeocoderServiceSpec.js | 18 +++++++------- packages/widgets/Source/Viewer/Viewer.js | 2 +- packages/widgets/Specs/Viewer/ViewerSpec.js | 8 +++---- 15 files changed, 39 insertions(+), 39 deletions(-) rename packages/engine/Source/Core/{IonGeocodeProvider.js => IonGeocodeProviderType.js} (85%) diff --git a/Apps/Sandcastle/gallery/3D Tiles Vertical Exaggeration.html b/Apps/Sandcastle/gallery/3D Tiles Vertical Exaggeration.html index 36f9cba9d603..292e07945f11 100644 --- a/Apps/Sandcastle/gallery/3D Tiles Vertical Exaggeration.html +++ b/Apps/Sandcastle/gallery/3D Tiles Vertical Exaggeration.html @@ -78,7 +78,7 @@

Loading...

animation: false, sceneModePicker: false, baseLayerPicker: false, - geocoder: Cesium.IonGeocodeProvider.GOOGLE, + geocoder: Cesium.IonGeocodeProviderType.GOOGLE, // The globe does not need to be displayed, // since the Photorealistic 3D Tiles include terrain globe: false, diff --git a/Apps/Sandcastle/gallery/AEC Clipping.html b/Apps/Sandcastle/gallery/AEC Clipping.html index 6c5ba834e71c..57c381068f9d 100644 --- a/Apps/Sandcastle/gallery/AEC Clipping.html +++ b/Apps/Sandcastle/gallery/AEC Clipping.html @@ -32,7 +32,7 @@ animation: false, sceneModePicker: false, baseLayerPicker: false, - geocoder: Cesium.IonGeocodeProvider.GOOGLE, + geocoder: Cesium.IonGeocodeProviderType.GOOGLE, // The globe does not need to be displayed, // since the Photorealistic 3D Tiles include terrain globe: false, diff --git a/Apps/Sandcastle/gallery/Bing Maps Labels Only.html b/Apps/Sandcastle/gallery/Bing Maps Labels Only.html index 8bebfcd63b1a..7f92188196ba 100644 --- a/Apps/Sandcastle/gallery/Bing Maps Labels Only.html +++ b/Apps/Sandcastle/gallery/Bing Maps Labels Only.html @@ -67,7 +67,7 @@ baseLayer: false, baseLayerPicker: false, infoBox: false, - geocoder: Cesium.IonGeocodeProvider.BING, + geocoder: Cesium.IonGeocodeProviderType.BING, }); const layers = viewer.imageryLayers; diff --git a/Apps/Sandcastle/gallery/Clamp Entities to Ground.html b/Apps/Sandcastle/gallery/Clamp Entities to Ground.html index 52b7bbef1fa9..07b1f9afd3d9 100644 --- a/Apps/Sandcastle/gallery/Clamp Entities to Ground.html +++ b/Apps/Sandcastle/gallery/Clamp Entities to Ground.html @@ -41,7 +41,7 @@ timeline: false, animation: false, baseLayerPicker: false, - geocoder: Cesium.IonGeocodeProvider.GOOGLE, + geocoder: Cesium.IonGeocodeProviderType.GOOGLE, }); const scene = viewer.scene; scene.globe.depthTestAgainstTerrain = true; diff --git a/Apps/Sandcastle/gallery/Globe Interior.html b/Apps/Sandcastle/gallery/Globe Interior.html index 2c02fa55daf3..9a4ff467d784 100644 --- a/Apps/Sandcastle/gallery/Globe Interior.html +++ b/Apps/Sandcastle/gallery/Globe Interior.html @@ -34,7 +34,7 @@ //Sandcastle_Begin const viewer = new Cesium.Viewer("cesiumContainer", { orderIndependentTranslucency: false, - geocoder: Cesium.IonGeocodeProvider.BING, + geocoder: Cesium.IonGeocodeProviderType.BING, }); const scene = viewer.scene; diff --git a/Apps/Sandcastle/gallery/Google Photorealistic 3D Tiles with Building Insert.html b/Apps/Sandcastle/gallery/Google Photorealistic 3D Tiles with Building Insert.html index 18c384a7b4d0..d93b4085a064 100644 --- a/Apps/Sandcastle/gallery/Google Photorealistic 3D Tiles with Building Insert.html +++ b/Apps/Sandcastle/gallery/Google Photorealistic 3D Tiles with Building Insert.html @@ -32,7 +32,7 @@ animation: false, sceneModePicker: false, baseLayerPicker: false, - geocoder: Cesium.IonGeocodeProvider.GOOGLE, + geocoder: Cesium.IonGeocodeProviderType.GOOGLE, // The globe does not need to be displayed, // since the Photorealistic 3D Tiles include terrain globe: false, diff --git a/Apps/Sandcastle/gallery/Google Photorealistic 3D Tiles.html b/Apps/Sandcastle/gallery/Google Photorealistic 3D Tiles.html index 93d337b076a0..59eec45e543f 100644 --- a/Apps/Sandcastle/gallery/Google Photorealistic 3D Tiles.html +++ b/Apps/Sandcastle/gallery/Google Photorealistic 3D Tiles.html @@ -32,7 +32,7 @@ animation: false, sceneModePicker: false, baseLayerPicker: false, - geocoder: Cesium.IonGeocodeProvider.GOOGLE, + geocoder: Cesium.IonGeocodeProviderType.GOOGLE, // The globe does not need to be displayed, // since the Photorealistic 3D Tiles include terrain globe: false, diff --git a/Apps/Sandcastle/gallery/Imagery Color To Alpha.html b/Apps/Sandcastle/gallery/Imagery Color To Alpha.html index 202c13ddfc3b..c28533a3c4a9 100644 --- a/Apps/Sandcastle/gallery/Imagery Color To Alpha.html +++ b/Apps/Sandcastle/gallery/Imagery Color To Alpha.html @@ -52,7 +52,7 @@ "use strict"; //Sandcastle_Begin const viewer = new Cesium.Viewer("cesiumContainer", { - geocoder: Cesium.IonGeocodeProvider.BING, + geocoder: Cesium.IonGeocodeProviderType.BING, }); const layers = viewer.scene.imageryLayers; diff --git a/Apps/Sandcastle/gallery/Imagery Layers Manipulation.html b/Apps/Sandcastle/gallery/Imagery Layers Manipulation.html index 49df7fd5c6d5..48a3efc177ab 100644 --- a/Apps/Sandcastle/gallery/Imagery Layers Manipulation.html +++ b/Apps/Sandcastle/gallery/Imagery Layers Manipulation.html @@ -106,7 +106,7 @@ //Sandcastle_Begin const viewer = new Cesium.Viewer("cesiumContainer", { baseLayerPicker: false, - geocoder: Cesium.IonGeocodeProvider.BING, + geocoder: Cesium.IonGeocodeProviderType.BING, }); const imageryLayers = viewer.imageryLayers; diff --git a/packages/engine/Source/Core/IonGeocodeProvider.js b/packages/engine/Source/Core/IonGeocodeProviderType.js similarity index 85% rename from packages/engine/Source/Core/IonGeocodeProvider.js rename to packages/engine/Source/Core/IonGeocodeProviderType.js index e78d1dce3139..2dc9bf35d775 100644 --- a/packages/engine/Source/Core/IonGeocodeProvider.js +++ b/packages/engine/Source/Core/IonGeocodeProviderType.js @@ -3,7 +3,7 @@ * * @enum {string} */ -const IonGeocodeProvider = { +const IonGeocodeProviderType = { /** * Google geocoder, for use with Google data. * @@ -30,4 +30,4 @@ const IonGeocodeProvider = { DEFAULT: "DEFAULT", }; -export default Object.freeze(IonGeocodeProvider); +export default Object.freeze(IonGeocodeProviderType); diff --git a/packages/engine/Source/Core/IonGeocoderService.js b/packages/engine/Source/Core/IonGeocoderService.js index 008ef8b0680b..db0cfe3dfc81 100644 --- a/packages/engine/Source/Core/IonGeocoderService.js +++ b/packages/engine/Source/Core/IonGeocoderService.js @@ -4,7 +4,7 @@ import defaultValue from "./defaultValue.js"; import defined from "./defined.js"; import DeveloperError from "./DeveloperError.js"; import Ion from "./Ion.js"; -import IonGeocodeProvider from "./IonGeocodeProvider.js"; +import IonGeocodeProviderType from "./IonGeocodeProviderType.js"; import PeliasGeocoderService from "./PeliasGeocoderService.js"; import Resource from "./Resource.js"; @@ -13,9 +13,9 @@ import Resource from "./Resource.js"; * @throws {DeveloperError} * @private */ -function validateIonGeocodeProvider(geocodeProvider) { +function validateIonGeocodeProviderType(geocodeProvider) { if ( - !Object.values(IonGeocodeProvider).some( + !Object.values(IonGeocodeProviderType).some( (value) => value === geocodeProvider, ) ) { @@ -24,9 +24,9 @@ function validateIonGeocodeProvider(geocodeProvider) { } const providerToParameterMap = Object.freeze({ - [IonGeocodeProvider.GOOGLE]: "google", - [IonGeocodeProvider.BING]: "bing", - [IonGeocodeProvider.DEFAULT]: undefined, + [IonGeocodeProviderType.GOOGLE]: "google", + [IonGeocodeProviderType.BING]: "bing", + [IonGeocodeProviderType.DEFAULT]: undefined, }); function providerToQueryParameter(provider) { @@ -48,7 +48,7 @@ function queryParameterToProvider(parameter) { * @param {Scene} options.scene The scene * @param {string} [options.accessToken=Ion.defaultAccessToken] The access token to use. * @param {string|Resource} [options.server=Ion.defaultServer] The resource to the Cesium ion API server. - * @param {IonGeocodeProvider} [options.geocodeProvider=IonGeocodeProvider.DEFAULT] The geocoder the Cesium ion API server should use to fulfill this request. + * @param {IonGeocodeProviderType} [options.geocodeProvider=IonGeocodeProviderType.DEFAULT] The geocoder the Cesium ion API server should use to fulfill this request. * * @see Ion */ @@ -61,9 +61,9 @@ function IonGeocoderService(options) { const geocodeProvider = defaultValue( options.geocodeProvider, - IonGeocodeProvider.DEFAULT, + IonGeocodeProviderType.DEFAULT, ); - validateIonGeocodeProvider(geocodeProvider); + validateIonGeocodeProviderType(geocodeProvider); const accessToken = defaultValue(options.accessToken, Ion.defaultAccessToken); const server = Resource.createIfNeeded( @@ -108,8 +108,8 @@ Object.defineProperties(IonGeocoderService.prototype, { /** * The geocoding service that Cesium ion API server should use to fulfill geocding requests. * @memberof IonGeocoderService.prototype - * @type {IonGeocodeProvider} - * @default IonGeocodeProvider.DEFAULT + * @type {IonGeocodeProviderType} + * @default IonGeocodeProviderType.DEFAULT */ geocodeProvider: { get: function () { @@ -118,7 +118,7 @@ Object.defineProperties(IonGeocoderService.prototype, { ); }, set: function (geocodeProvider) { - validateIonGeocodeProvider(geocodeProvider); + validateIonGeocodeProviderType(geocodeProvider); const query = { ...this._pelias.url.queryParameters, geocoder: providerToQueryParameter(geocodeProvider), diff --git a/packages/engine/Source/Scene/createGooglePhotorealistic3DTileset.js b/packages/engine/Source/Scene/createGooglePhotorealistic3DTileset.js index ef35c0c38e31..8378df0050d5 100644 --- a/packages/engine/Source/Scene/createGooglePhotorealistic3DTileset.js +++ b/packages/engine/Source/Scene/createGooglePhotorealistic3DTileset.js @@ -18,7 +18,7 @@ import Resource from "../Core/Resource.js"; * * @example * const viewer = new Cesium.Viewer("cesiumContainer", { - * geocoder: Cesium.IonGeocodeProvider.GOOGLE + * geocoder: Cesium.IonGeocodeProviderType.GOOGLE * }); * * try { @@ -33,7 +33,7 @@ import Resource from "../Core/Resource.js"; * Cesium.GoogleMaps.defaultApiKey = "your-api-key"; * * const viewer = new Cesium.Viewer("cesiumContainer". { - * geocoder: Cesium.IonGeocodeProvider.GOOGLE + * geocoder: Cesium.IonGeocodeProviderType.GOOGLE * }); * * try { diff --git a/packages/engine/Specs/Core/IonGeocoderServiceSpec.js b/packages/engine/Specs/Core/IonGeocoderServiceSpec.js index 0bdc02b18692..d2254501bf59 100644 --- a/packages/engine/Specs/Core/IonGeocoderServiceSpec.js +++ b/packages/engine/Specs/Core/IonGeocoderServiceSpec.js @@ -3,7 +3,7 @@ import { GeocoderService, GeocodeType, Ion, - IonGeocodeProvider, + IonGeocodeProviderType, IonGeocoderService, } from "../../index.js"; @@ -28,13 +28,13 @@ describe("Core/IonGeocoderService", function () { expect(service._accessToken).toEqual(Ion.defaultAccessToken); expect(service._server.url).toEqual(Ion.defaultServer.url); - expect(service.geocodeProvider).toEqual(IonGeocodeProvider.DEFAULT); + expect(service.geocodeProvider).toEqual(IonGeocodeProviderType.DEFAULT); }); it("creates with specified parameters", function () { const accessToken = "123456"; const server = "http://not.ion.invalid/"; - const geocodeProvider = IonGeocodeProvider.GOOGLE; + const geocodeProvider = IonGeocodeProviderType.GOOGLE; const service = new IonGeocoderService({ accessToken: accessToken, @@ -74,30 +74,30 @@ describe("Core/IonGeocoderService", function () { it("setting geocodeProvider updates _pelias.url for GOOGLE", function () { const service = new IonGeocoderService({ scene, - geocoder: IonGeocodeProvider.DEFAULT, + geocoder: IonGeocodeProviderType.DEFAULT, }); - service.geocodeProvider = IonGeocodeProvider.GOOGLE; + service.geocodeProvider = IonGeocodeProviderType.GOOGLE; expect(service._pelias.url.queryParameters["geocoder"]).toEqual("google"); }); it("setting geocodeProvider updates _pelias.url for BING", function () { const service = new IonGeocoderService({ scene, - geocoder: IonGeocodeProvider.DEFAULT, + geocoder: IonGeocodeProviderType.DEFAULT, }); - service.geocodeProvider = IonGeocodeProvider.BING; + service.geocodeProvider = IonGeocodeProviderType.BING; expect(service._pelias.url.queryParameters["geocoder"]).toEqual("bing"); }); it("setting geocodeProvider updates _pelias.url for DEFAULT", function () { const service = new IonGeocoderService({ scene, - geocoder: IonGeocodeProvider.GOOGLE, + geocoder: IonGeocodeProviderType.GOOGLE, }); - service.geocodeProvider = IonGeocodeProvider.DEFAULT; + service.geocodeProvider = IonGeocodeProviderType.DEFAULT; const queryParameters = service._pelias.url.queryParameters; expect(queryParameters.geocoder).toBeUndefined(); // Make sure that it isn't 'geocoder: undefined' diff --git a/packages/widgets/Source/Viewer/Viewer.js b/packages/widgets/Source/Viewer/Viewer.js index 44e32292f0ab..a1d1cbd970e6 100644 --- a/packages/widgets/Source/Viewer/Viewer.js +++ b/packages/widgets/Source/Viewer/Viewer.js @@ -281,7 +281,7 @@ function enableVRUI(viewer, enabled) { * @property {boolean} [baseLayerPicker=true] If set to false, the BaseLayerPicker widget will not be created. * @property {boolean} [fullscreenButton=true] If set to false, the FullscreenButton widget will not be created. * @property {boolean} [vrButton=false] If set to true, the VRButton widget will be created. - * @property {boolean|IonGeocodeProvider|GeocoderService[]} [geocoder=IonGeocodeProvider.DEFAULT] The geocoding service or services to use when searching with the Geocoder widget. If set to false, the Geocoder widget will not be created. + * @property {boolean|IonGeocodeProviderType|GeocoderService[]} [geocoder=IonGeocodeProviderType.DEFAULT] The geocoding service or services to use when searching with the Geocoder widget. If set to false, the Geocoder widget will not be created. * @property {boolean} [homeButton=true] If set to false, the HomeButton widget will not be created. * @property {boolean} [infoBox=true] If set to false, the InfoBox widget will not be created. * @property {boolean} [sceneModePicker=true] If set to false, the SceneModePicker widget will not be created. diff --git a/packages/widgets/Specs/Viewer/ViewerSpec.js b/packages/widgets/Specs/Viewer/ViewerSpec.js index 8bb508d100b7..01eff7afc8b9 100644 --- a/packages/widgets/Specs/Viewer/ViewerSpec.js +++ b/packages/widgets/Specs/Viewer/ViewerSpec.js @@ -18,7 +18,7 @@ import { ImageryLayerCollection, SceneMode, ShadowMode, - IonGeocodeProvider, + IonGeocodeProviderType, IonGeocoderService, } from "@cesium/engine"; @@ -356,16 +356,16 @@ describe( expect(viewer.geocoder.viewModel._geocoderServices.length).toBe(1); }); - it("constructs geocoder with IonGeocodeProvider", function () { + it("constructs geocoder with IonGeocodeProviderType", function () { viewer = createViewer(container, { - geocoder: IonGeocodeProvider.GOOGLE, + geocoder: IonGeocodeProviderType.GOOGLE, }); expect(viewer.geocoder).toBeDefined(); expect(viewer.geocoder.viewModel._geocoderServices.length).toBe(1); const geocoderService = viewer.geocoder.viewModel._geocoderServices[0]; expect(geocoderService).toBeInstanceOf(IonGeocoderService); expect(geocoderService.geocodeProvider).toEqual( - IonGeocodeProvider.GOOGLE, + IonGeocodeProviderType.GOOGLE, ); }); From a7117d8c09d4a9a9cc426ab6b222129b36d14dbe Mon Sep 17 00:00:00 2001 From: Mark Dane Date: Thu, 14 Nov 2024 11:42:48 -0500 Subject: [PATCH 072/175] Fix release test bug where SceneMode was set to 2d somehow --- packages/engine/Specs/Scene/SkyBoxSpec.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/engine/Specs/Scene/SkyBoxSpec.js b/packages/engine/Specs/Scene/SkyBoxSpec.js index ec9749d2eb33..5b6e3440f839 100644 --- a/packages/engine/Specs/Scene/SkyBoxSpec.js +++ b/packages/engine/Specs/Scene/SkyBoxSpec.js @@ -11,8 +11,6 @@ describe( let loadedImage; beforeAll(function () { - scene = createScene(); - return Resource.fetchImage("./Data/Images/Blue.png").then( function (image) { loadedImage = image; @@ -25,6 +23,7 @@ describe( }); beforeEach(function () { + scene = createScene(); scene.mode = SceneMode.SCENE3D; }); From ea4287569517ee799f8c75bfbdb7982fefcc8cc0 Mon Sep 17 00:00:00 2001 From: Mark Dane Date: Thu, 14 Nov 2024 11:48:04 -0500 Subject: [PATCH 073/175] Clean up --- packages/engine/Specs/Scene/SkyBoxSpec.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/engine/Specs/Scene/SkyBoxSpec.js b/packages/engine/Specs/Scene/SkyBoxSpec.js index 5b6e3440f839..9691fb035594 100644 --- a/packages/engine/Specs/Scene/SkyBoxSpec.js +++ b/packages/engine/Specs/Scene/SkyBoxSpec.js @@ -18,10 +18,6 @@ describe( ); }); - afterAll(function () { - scene.destroyForSpecs(); - }); - beforeEach(function () { scene = createScene(); scene.mode = SceneMode.SCENE3D; @@ -30,6 +26,7 @@ describe( afterEach(function () { skyBox = skyBox && skyBox.destroy(); scene.skyBox = undefined; + scene.destroyForSpecs(); }); it("draws a sky box from Images", function () { From 50dfe46a90342611a1fc5c5d52ceb50fcfd54496 Mon Sep 17 00:00:00 2001 From: jjspace <8007967+jjspace@users.noreply.github.com> Date: Thu, 14 Nov 2024 13:18:25 -0500 Subject: [PATCH 074/175] small cleanup and removing code we won't use --- Apps/Sandcastle/gallery/iTwin Demo.html | 103 +----------------- itwin-oauth-demo/server.js | 48 -------- packages/engine/Source/Core/ITwin.js | 67 ++++-------- .../Source/Scene/createIModel3DTileset.js | 26 ++--- 4 files changed, 33 insertions(+), 211 deletions(-) diff --git a/Apps/Sandcastle/gallery/iTwin Demo.html b/Apps/Sandcastle/gallery/iTwin Demo.html index ef4991471367..eeea23b17f51 100644 --- a/Apps/Sandcastle/gallery/iTwin Demo.html +++ b/Apps/Sandcastle/gallery/iTwin Demo.html @@ -30,95 +30,17 @@ window.startup = async function (Cesium) { "use strict"; //Sandcastle_Begin - const popup = (url) => { - // based on https://gist.github.com/gauravtiwari/2ae9f44aee281c759fe5a66d5c2721a2 - const windowArea = { - width: 500, - height: 600, - }; - - windowArea.left = Math.floor( - window.screenX + (window.outerWidth - windowArea.width) / 2, - ); - windowArea.top = Math.floor( - window.screenY + (window.outerHeight - windowArea.height) / 8, - ); - - const sep = url.indexOf("?") !== -1 ? "&" : "?"; - const fullUrl = `${url}${sep}`; - const windowOpts = `toolbar=0,scrollbars=1,status=1,resizable=1,location=1,menuBar=0, - width=${windowArea.width},height=${windowArea.height}, - left=${windowArea.left},top=${windowArea.top}`; - - const authWindow = window.open(fullUrl, "popup", windowOpts); - - // Listen to message from child window - const authPromise = new Promise((resolve, reject) => { - window.addEventListener( - "message", - (e) => { - console.log("message", e); - // TODO: if we go this route we may want this back to make sure - // it's coming from wherever we host this - // if (e.origin !== window.SITE_DOMAIN) { - // authWindow.close(); - // reject("Not allowed"); - // } - - if (e.data.auth) { - resolve(e.data.auth); - authWindow.close(); - } else { - authWindow.close(); - reject("Unauthorised"); - } - }, - false, - ); - }); - return authPromise; - }; - - // ===== Web app oauth flow ===== - - // const clientId = "webapp-0MsbOTn56gKXMoI1WsJ0SoVEn"; - // const redirectUri = "http://localhost:3000"; - - // const result = await popup( - // `https://ims.bentley.com/connect/authorize?response_type=code&client_id=${clientId}&redirect_uri=${redirectUri}&scope=itwin-platform`, - // ).catch((error) => { - // console.error(error); - // throw new Error("OAuth failed"); - // }); - // const accessCode = result.code; - // console.log("popup returned", result); - - // ===== Service app request ===== - const serviceResponse = await fetch("http://localhost:3000/service"); - const serviceResult = await serviceResponse.json(); - const { token: accessCode } = serviceResult; - - // must be created for the Start export route if you want to create new exports - // Needs to have the mesh-export:modify scope not just mesh-export:read - let accessToken = - "Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IkJlbnRsZXlJTVNfMjAyNCIsInBpLmF0bSI6ImE4bWUifQ.eyJzY29wZSI6WyJtZXNoLWV4cG9ydDpyZWFkIiwibWVzaC1leHBvcnQ6bW9kaWZ5Il0sImNsaWVudF9pZCI6Iml0d2luLWRldmVsb3Blci1jb25zb2xlIiwiYXVkIjpbImh0dHBzOi8vaW1zLmJlbnRsZXkuY29tL2FzL3Rva2VuLm9hdXRoMiIsImh0dHBzOi8vaW1zb2lkYy5iZW50bGV5LmNvbS9hcy90b2tlbi5vYXV0aDIiLCJodHRwczovL2ltc29pZGMuYmVudGxleS5jb20vcmVzb3VyY2VzIiwiYmVudGxleS1hcGktbWFuYWdlbWVudCJdLCJzdWIiOiJjMWM1MzRhNy0zZDk2LTQ2MzMtYjY5ZC1jMGEzNzE5OWQwZGUiLCJyb2xlIjoiQkVOVExFWV9FTVBMT1lFRSIsIm9yZyI6ImZhYjk3NzRiLWIzMzgtNGNjMi1hNmM5LTQ1OGJkZjdmOTY2YSIsInN1YmplY3QiOiJjMWM1MzRhNy0zZDk2LTQ2MzMtYjY5ZC1jMGEzNzE5OWQwZGUiLCJpc3MiOiJodHRwczovL2ltcy5iZW50bGV5LmNvbSIsImVudGl0bGVtZW50IjpbIkJFTlRMRVlfTEVBUk4iLCJJTlRFUk5BTCIsIlNFTEVDVF8yMDA2IiwiQkVOIiwiQkROIl0sInByZWZlcnJlZF91c2VybmFtZSI6Ikpvc2guUm91emVyQGJlbnRsZXkuY29tIiwiZ2l2ZW5fbmFtZSI6Ikpvc2giLCJzaWQiOiJHMGZTamlOXzBMRjE1Vy1IYnRTaWtoeDNuSXMuU1UxVExVSmxiblJzWlhrdFZWTS5LbWlWLlF2UExVcGJyc2R3engwWGxPbkF3eTFTT0IiLCJuYmYiOjE3MzA4MzgyNDIsInVsdGltYXRlX3NpdGUiOiIxMDAxMzg5MTE3IiwidXNhZ2VfY291bnRyeV9pc28iOiJVUyIsImF1dGhfdGltZSI6MTczMDgzODU0MiwibmFtZSI6Ikpvc2guUm91emVyQGJlbnRsZXkuY29tIiwib3JnX25hbWUiOiJCZW50bGV5IFN5c3RlbXMgSW5jIiwiZmFtaWx5X25hbWUiOiJSb3V6ZXIiLCJlbWFpbCI6Ikpvc2guUm91emVyQGJlbnRsZXkuY29tIiwiZXhwIjoxNzMwODQyMTQyfQ.N_WrgjL2bqxdNLEM5nHh4Fg-FzeA-qxxpryaoaMKz8onnpgzmp-X8dyQ1TyMRqKQY99iLypE9bU45DwRJs7vBgNQ73d53SkC9TKGYn8AAOTJz8c_oEHgkoTEDaaLsMPCw88tqZ34pY0e0oIHIofVDTCvwlzwaJPADfkIxz-8GzhIt2WrXR7f6LWBFSlWNrtNhIr9Tz7UNaLOh97_3fS9KVU1I084CpBga9cj_mjGBeki7mIEQvpqMj8x2bJPae_c6WrPEjKLayrOzq4q0X0Kvle0ZFAm-Se9MFCusTFS51bYrI3IsjagGeIP2U06zzcMJ22NylomE60hz6GK_4uB3g"; - - accessToken = `Bearer ${accessCode}`; + const { token } = await serviceResponse.json(); + const accessToken = `Bearer ${token}`; Cesium.ITwin.defaultAccessToken = accessToken; // this is the iModel in the "Hello iTwinCesium" iTwin that we should all have access to // https://developer.bentley.com/my-itwins/b4a30036-0456-49ea-a439-3fcd9365e24e/home/ // const imodelId = "2852c3d7-00c3-4b5d-a0ce-82bbde4f061e"; const imodelId = "88673c1d-12b8-48f1-8beb-5000d0edbd0b"; - const changesetId = ""; - // const knownExportId = undefined; - // const knownExportId = "ab9953b2-bc8e-48ac-a5b0-5d43d68593e8"; - - const delay = (ms) => new Promise((res) => setTimeout(res, ms)); - // Grabbed mapping from the iTwin Viewer const classes = { 2199023255632: "Building Roof", @@ -196,24 +118,7 @@ const statusOutput = document.querySelector("#status"); async function init() { - // TODO: just for testing - // await Cesium.ITwin.deleteExport("8bd9c52e-b379-4f7d-bf39-c45954030b26"); - // console.log(await Cesium.ITwin.getExports(imodelId)); - statusOutput.innerText = "Starting export"; - // const exportId = - // knownExportId ?? - // (await Cesium.ITwin.createExportForModelId( - // imodelId, - // "", - // accessToken, - // )); - - // if (!exportId) { - // console.error("No export id returned"); - // return; - // } - // console.log("Using export id", exportId); const start = Date.now(); @@ -224,6 +129,8 @@ changesetId, ); if (!Cesium.defined(tileset)) { + // TODO: this is temporary, we should not have to call the Start Export route ever after + // auto generation is set up statusOutput.innerText = "Starting export"; const exportId = await Cesium.ITwin.createExportForModelId( imodelId, @@ -234,7 +141,6 @@ tileset = await Cesium.createIModel3DTileset.fromExportId(exportId); } - // const tileset = await Cesium.createIModel3DTileset(exportId); scene.primitives.add(tileset); tileset.colorBlendMode = Cesium.Cesium3DTileColorBlendMode.REPLACE; @@ -267,7 +173,6 @@ } console.error(error); }); - //Sandcastle_End Sandcastle.finishedLoading(); }; diff --git a/itwin-oauth-demo/server.js b/itwin-oauth-demo/server.js index 790b96c9958a..8a935343622e 100644 --- a/itwin-oauth-demo/server.js +++ b/itwin-oauth-demo/server.js @@ -5,16 +5,11 @@ import { exit } from "process"; import { fileURLToPath } from "url"; let config = { - webapp: { - clientId: "", - clientSecret: "", - }, serviceapp: { clientId: "", clientSecret: "", }, port: 3000, - redirectUri: "http://localhost:3000", }; const __dirname = dirname(fileURLToPath(import.meta.url)); @@ -31,49 +26,6 @@ try { const app = express(); const port = config.port ?? 3000; -const redirectUri = config.redirectUri ?? "http://localhost:3000"; - -// eslint-disable-next-line no-unused-vars -app.get("/", async (req, res) => { - res.sendFile(join(__dirname, "./index.html")); -}); - -app.get("/token", async (req, res) => { - console.log("/token request received"); - const { code } = req.query; - - if (!code) { - res.status(404).send("Code missing"); - } - - const body = new URLSearchParams(); - body.set("grant_type", "authorization_code"); - body.set("client_id", config.webapp.clientId); - body.set("client_secret", config.webapp.clientSecret); - body.set("code", code); - body.set("redirect_uri", redirectUri); - - const response = await fetch("https://ims.bentley.com/connect/token", { - method: "POST", - body, - }); - - const result = await response.json(); - - if (!response.ok || !result) { - console.log(" bad response/no result"); - res.status(response.status).send(); - return; - } - const { access_token } = result; - if (access_token) { - console.log(" token acquired, returned"); - res.status(200).send({ token: access_token }); - return; - } - console.log(" token not found"); - res.status(404).send("token not found"); -}); // eslint-disable-next-line no-unused-vars app.get("/service", async (req, res) => { diff --git a/packages/engine/Source/Core/ITwin.js b/packages/engine/Source/Core/ITwin.js index e3ed43b1ee68..51a89e15668d 100644 --- a/packages/engine/Source/Core/ITwin.js +++ b/packages/engine/Source/Core/ITwin.js @@ -20,10 +20,9 @@ const ExportStatus = Object.freeze({ * @enum {string} */ const ExportType = Object.freeze({ - "3DFT": "3DFT", - GLTF: "GLTF", IMODEL: "IMODEL", CESIUM: "CESIUM", + "3DTILES": "3DTILES", }); /** @@ -75,6 +74,8 @@ const ExportType = Object.freeze({ /** * Default settings for accessing the iTwin platform. * + * @experimental + * * @see createIModel3DTileset * @namespace ITwin */ @@ -91,6 +92,8 @@ const ITwin = {}; * `itwin-platform` if we want to use the iModel shares ourselves GET /imodels/{id}/shares * Seems the `itwin-platform` scope should apply to everything but the docs are a little unclear * + * @experimental + * * @type {string|undefined} */ ITwin.defaultAccessToken = undefined; @@ -98,6 +101,8 @@ ITwin.defaultAccessToken = undefined; /** * Gets or sets the default iTwin API endpoint. * + * @experimental + * * @type {string|Resource} * @default https://api.bentley.com */ @@ -106,6 +111,10 @@ ITwin.apiEndpoint = new Resource({ }); /** + * Get the export object for the specified export id + * + * @experimental + * * @param {string} exportId */ ITwin.getExport = async function (exportId) { @@ -146,6 +155,8 @@ ITwin.getExport = async function (exportId) { /** * Get the list of exports for the given iModel + changeset * + * @experimental + * * @param {string} iModelId * @param {string} [changesetId] */ @@ -161,7 +172,8 @@ ITwin.getExports = async function (iModelId, changesetId) { }; // obtain export for specified export id - let url = `${ITwin.apiEndpoint}mesh-export/?iModelId=${iModelId}&exportType=CESIUM&$top=1`; + // TODO: if we do include the clientVersion what should it be set to? can we sync it with the package.json? + let url = `${ITwin.apiEndpoint}mesh-export/?iModelId=${iModelId}&exportType=${ExportType["3DTILES"]}&$top=1&client=CesiumJS&clientVersion=1.123`; if (defined(changesetId) && changesetId !== "") { url += `&changesetId=${changesetId}`; } @@ -194,6 +206,12 @@ ITwin.getExports = async function (iModelId, changesetId) { /** * Start the export process for the given iModel + changeset. * + * TODO: REMOVE THIS FUNCTION! Auto generation of exports for the 3DTILES type is planned very soon + * and will be the desired way of interacting with iModels through exports. This function is here + * just while we continue testing during the PR process. + * + * @experimental + * * @param {string} iModelId * @param {string} [changesetId] */ @@ -214,7 +232,7 @@ ITwin.createExportForModelId = async function (iModelId, changesetId) { body: JSON.stringify({ iModelId, changesetId, - exportType: "CESIUM", + exportType: ExportType["3DTILES"], }), }; @@ -254,46 +272,5 @@ ITwin.createExportForModelId = async function (iModelId, changesetId) { return result.export.id; }; -/** - * Delete the specified export - * - * TODO: I'm not sure if we want this or not. Might belong better as an APP level function - * I just started creating helpers for all the routes under the `mesh-export` API - * for ease of access during testing - * - * @param {string} exportId - */ -ITwin.deleteExport = async function (exportId) { - if (!defined(ITwin.defaultAccessToken)) { - throw new DeveloperError("Must set ITwin.defaultAccessToken first"); - } - const headers = { - Authorization: ITwin.defaultAccessToken, - Accept: "application/vnd.bentley.itwin-platform.v1+json", - }; - - // obtain export for specified export id - const url = `${ITwin.apiEndpoint}mesh-export/${exportId}`; - - const response = await fetch(url, { method: "DELETE", headers }); - if (!response.ok) { - const result = await response.json(); - if (response.status === 401) { - throw new RuntimeError( - `Unauthorized, bad token, wrong scopes or headers bad. ${result.error.details[0].code}`, - ); - } else if (response.status === 404) { - throw new RuntimeError("Export not found"); - } else if (response.status === 422) { - throw new RuntimeError( - `Unprocessable Entity: ${result.error.code} ${result.error.message}`, - ); - } else if (response.status === 429) { - throw new RuntimeError("Too many requests"); - } - throw new RuntimeError(`Unknown request failure ${response.status}`); - } -}; - export default ITwin; export { ExportStatus, ExportType }; diff --git a/packages/engine/Source/Scene/createIModel3DTileset.js b/packages/engine/Source/Scene/createIModel3DTileset.js index 7bbefa6e0e0f..9e5908bee402 100644 --- a/packages/engine/Source/Scene/createIModel3DTileset.js +++ b/packages/engine/Source/Scene/createIModel3DTileset.js @@ -16,7 +16,7 @@ function delay(ms) { async function loadExport(exportObj, options) { let status = exportObj.status; - if (exportObj.request.exportType !== ExportType.CESIUM) { + if (exportObj.request.exportType !== ExportType["3DTILES"]) { // This is an undocumented value but I think it's the only one we want to load // TODO: should we even be checking this? throw new Error(`Wrong export type ${exportObj.request.exportType}`); @@ -55,6 +55,7 @@ const createIModel3DTileset = {}; * Creates a {@link Cesium3DTileset} instance for the Google Photorealistic 3D Tiles tileset. * * @function + * @experimental * * @param {string} exportId * @param {Cesium3DTileset.ConstructorOptions} [options] An object describing initialization options. @@ -63,17 +64,7 @@ const createIModel3DTileset = {}; * @see ITwin * * @example - * // Use your own iTwin API key for mesh export - * Cesium.ITwin.defaultApiKey = "your-api-key"; - * - * const viewer = new Cesium.Viewer("cesiumContainer"); - * - * try { - * const tileset = await Cesium.createIModel3DTileset(); - * viewer.scene.primitives.add(tileset)); - * } catch (error) { - * console.log(`Error creating tileset: ${error}`); - * } + * TODO: example after API finalized */ createIModel3DTileset.fromExportId = async function (exportId, options) { if (!defined(ITwin.defaultAccessToken)) { @@ -98,12 +89,9 @@ createIModel3DTileset.fromExportId = async function (exportId, options) { * with the export id directly * * @example - * // Try to load the corresponding tileset export or create it if it doesn't exist - * let tileset = await Cesium.createIModel3DTileset.fromModelIdimodelId, changesetId); - * if (!Cesium.defined(tileset)) { - * const exportId = await Cesium.ITwin.createExportForModelId(imodelId, changesetId, accessToken); - * tileset = await Cesium.createIModel3DTileset(exportId); - * } + * TODO: example after API finalized + * + * @experimental * * @param {string} iModelId * @param {string} changesetId @@ -116,7 +104,7 @@ createIModel3DTileset.fromModelId = async function ( ) { const { exports } = await ITwin.getExports(iModelId, changesetId); const cesiumExport = exports.find( - (exportObj) => exportObj.request?.exportType === ExportType.CESIUM, + (exportObj) => exportObj.request?.exportType === ExportType["3DTILES"], ); if (!defined(cesiumExport)) { return; From 978a2984f8e3cf96cfdee78781112ceb3d2983a7 Mon Sep 17 00:00:00 2001 From: Jeshurun Hembd Date: Thu, 14 Nov 2024 14:32:32 -0500 Subject: [PATCH 075/175] Fix helper function call in Texture --- packages/engine/Source/Renderer/Texture.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/engine/Source/Renderer/Texture.js b/packages/engine/Source/Renderer/Texture.js index d65fa422b2a6..8d9135fb7764 100644 --- a/packages/engine/Source/Renderer/Texture.js +++ b/packages/engine/Source/Renderer/Texture.js @@ -941,7 +941,14 @@ Texture.prototype.copyFrom = function (options) { if (!uploaded) { if (defined(source.arrayBufferView)) { - loadPartialBufferSource(this, source, xOffset, yOffset, width, height); + loadPartialBufferSource( + this, + source.arrayBufferView, + xOffset, + yOffset, + width, + height, + ); } else { loadPartialImageSource(this, source, xOffset, yOffset); } From dc2b2fd81663edd3abe2055aa5948fe28784e32a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 15 Nov 2024 01:45:09 +0000 Subject: [PATCH 076/175] Bump slackapi/slack-github-action from 1.27.0 to 2.0.0 Bumps [slackapi/slack-github-action](https://github.com/slackapi/slack-github-action) from 1.27.0 to 2.0.0. - [Release notes](https://github.com/slackapi/slack-github-action/releases) - [Commits](https://github.com/slackapi/slack-github-action/compare/v1.27.0...v2.0.0) --- updated-dependencies: - dependency-name: slackapi/slack-github-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 376bc86d12b8..c39601d15129 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -14,7 +14,7 @@ jobs: steps: - name: message result in slack id: slack - uses: slackapi/slack-github-action@v1.27.0 + uses: slackapi/slack-github-action@v2.0.0 env: SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }} with: From 10a8c9804343b5d0d88030d7f758cd764a24723b Mon Sep 17 00:00:00 2001 From: ggetz Date: Fri, 15 Nov 2024 10:04:01 -0500 Subject: [PATCH 077/175] Update CHANGES.md --- CHANGES.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index b6edec4cbe75..47b8c5a262e0 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -8,6 +8,12 @@ - Fix label rendering bug in WebGL1 contexts. [#12301](https://github.com/CesiumGS/cesium/pull/12301) +#### @cesium/widgets + +##### Fixes :wrench: + +- Added a `DeveloperError` when `globe` is set to `false` and a `baseLayer` is provided in `Viewer` options. This prevents errors caused by attempting to use a `baseLayer` without a globe. [#12274](https://github.com/CesiumGS/cesium/pull/12274) + ### 1.123.1 - 2024-11-07 #### @cesium/engine @@ -54,11 +60,8 @@ - Fix flickering issue caused by bounding sphere retrieval being blocked by the bounding sphere of another entity. [#12230](https://github.com/CesiumGS/cesium/pull/12230) - Fixed `ImageBasedLighting.imageBasedLightingFactor` not affecting lighting. [#12129](https://github.com/CesiumGS/cesium/pull/12129) - - Fix error with normalization of corner points for lines and corridors with collinear points. [#12255](https://github.com/CesiumGS/cesium/pull/12255) -- Added a `DeveloperError` when `globe` is set to `false` and a `baseLayer` is provided in `Viewer` options. This prevents errors caused by attempting to use a `baseLayer` without a globe.[#12274](https://github.com/CesiumGS/cesium/pull/12274) - ### 1.122 - 2024-10-01 #### @cesium/engine From fbe54171c8f54075a4a567bec3e6a10e562a2c13 Mon Sep 17 00:00:00 2001 From: jjspace <8007967+jjspace@users.noreply.github.com> Date: Fri, 15 Nov 2024 11:52:27 -0500 Subject: [PATCH 078/175] add snippets file for vscode convenience --- .vscode/cesiumjs.code-snippets | 44 ++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 .vscode/cesiumjs.code-snippets diff --git a/.vscode/cesiumjs.code-snippets b/.vscode/cesiumjs.code-snippets new file mode 100644 index 000000000000..154d4b1ba084 --- /dev/null +++ b/.vscode/cesiumjs.code-snippets @@ -0,0 +1,44 @@ +{ + // Place your workspace snippets here. Each snippet is defined under a snippet name and has a scope, prefix, body and + // description. Add comma separated ids of the languages where the snippet is applicable in the scope field. If scope + // is left empty or omitted, the snippet gets applied to all languages. The prefix is what is + // used to trigger the snippet and the body will be expanded and inserted. Possible variables are: + // $1, $2 for tab stops, $0 for the final cursor position, and ${1:label}, ${2:another} for placeholders. + // Placeholders with the same ids are connected. + // Example: + // "Print to console": { + // "scope": "javascript,typescript", + // "prefix": "log", + // "body": [ + // "console.log('$1');", + // "$2" + // ], + // "description": "Log output to console" + // } + "Debug Pragma": { + "scope": "javascript", + "prefix": "pragdebug", + "description": "Insert or wrap selection with debug pragma comments", + "body": [ + "//>>includeStart('debug', pragmas.debug);", + "${TM_SELECTED_TEXT}$0", + "//>>includeEnd('debug')" + ] + }, + "Check type": { + "prefix": "check", + "body": [ + "Check.typeOf.${2:string}(\"${1:value}\", ${1:value});" + ], + "description": "Check Type of a variable" + }, + "Check type if defined": { + "prefix": "checkifdef", + "body": [ + "if (defined(${1:value})) {", + " Check.typeOf.${2:string}(\"${1:value}\", ${1:value});", + "}" + ], + "description": "Check type of a variable if it's defined" + } +} From d9e3859a0f8b5e4c61e504aa24e435df3fc89f43 Mon Sep 17 00:00:00 2001 From: ggetz Date: Fri, 15 Nov 2024 13:37:24 -0500 Subject: [PATCH 079/175] Documentation tweaks --- packages/engine/Source/Core/TrackingReferenceFrame.js | 2 +- packages/engine/Source/DataSources/Entity.js | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/packages/engine/Source/Core/TrackingReferenceFrame.js b/packages/engine/Source/Core/TrackingReferenceFrame.js index 5ca7dce4053d..4ad418e96b7a 100644 --- a/packages/engine/Source/Core/TrackingReferenceFrame.js +++ b/packages/engine/Source/Core/TrackingReferenceFrame.js @@ -19,7 +19,7 @@ const TrackingReferenceFrame = { /** * The entity's inertial reference frame. If entity has no defined orientation * property, a {@link VelocityOrientationProperty} is used instead, thus - * falling back to TrackingReferenceFrame.VELOCITY. + * falling back to TrackingReferenceFrame.VELOCITY. * When selected, the auto-detect algorithm is overridden. * * @type {number} diff --git a/packages/engine/Source/DataSources/Entity.js b/packages/engine/Source/DataSources/Entity.js index b57d2568c1da..d8c9fab17f9e 100644 --- a/packages/engine/Source/DataSources/Entity.js +++ b/packages/engine/Source/DataSources/Entity.js @@ -74,10 +74,7 @@ function createPropertyTypeDescriptor(name, Type) { * @property {string} [name] A human readable name to display to users. It does not have to be unique. * @property {TimeIntervalCollection} [availability] The availability, if any, associated with this object. * @property {boolean} [show] A boolean value indicating if the entity and its children are displayed. - * @property {TrackingReferenceFrame} [trackingReferenceFrame=TrackingReferenceFrame.AUTODETECT] The reference frame used when this entity is being tracked. - * If undefined, an auto-detect algorithm is used: near-surface slow moving entities are tracked using the local - * east-north-up reference frame, whereas fast moving entities such as satellites are tracked using VVLH (Vehicle Velocity, - * Local Horizontal). + * @property {TrackingReferenceFrame} [trackingReferenceFrame=TrackingReferenceFrame.AUTODETECT] The reference frame used when this entity is being tracked.
If undefined, reference frame is determined based on entity velocity: near-surface slow moving entities are tracked using the local east-north-up reference frame, whereas fast moving entities such as satellites are tracked using VVLH (Vehicle Velocity, Local Horizontal). * @property {Property | string} [description] A string Property specifying an HTML description for this entity. * @property {PositionProperty | Cartesian3 | CallbackPositionProperty} [position] A Property specifying the entity position. * @property {Property | Quaternion} [orientation=Transforms.eastNorthUpToFixedFrame(position)] A Property specifying the entity orientation in respect to Earth-fixed-Earth-centered (ECEF). If undefined, east-north-up at entity position is used. From 993de846e69ad36ea07a44348f21b44ba44b6106 Mon Sep 17 00:00:00 2001 From: jjspace <8007967+jjspace@users.noreply.github.com> Date: Fri, 15 Nov 2024 15:23:42 -0500 Subject: [PATCH 080/175] add a mention in the docs --- Documentation/Contributors/VSCodeGuide/README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Documentation/Contributors/VSCodeGuide/README.md b/Documentation/Contributors/VSCodeGuide/README.md index c759beb8da91..51d4931c6c3c 100644 --- a/Documentation/Contributors/VSCodeGuide/README.md +++ b/Documentation/Contributors/VSCodeGuide/README.md @@ -46,6 +46,10 @@ restart VSCode after you are done installing extensions. - **[Shader languages support for VS Code](https://marketplace.visualstudio.com/items?itemName=slevesque.shader)** by slevesque - This extension provides syntax highlighting for CesiumJS's shader code. - **[glTF Extension for VS Code](https://marketplace.visualstudio.com/items?itemName=cesium.gltf-vscode)** by CesiumJS.org - This extension adds features for previewing and editing 3D models in glTF files. +## Snippets + +We have a small (but growing) collection of snippets to make it easier to write code in CesiumJS. For example, `pragdebug` which will expand to the debug pragma we use to strip out code in production builds. These are stored in `.vscode/cesiumjs.code-snippets`. Refer to that file for the full list available and feel free to add to it for common code constructs you find yourself writing. + ## VSCode Tasks and Files You can launch any of CesiumJS's npm tasks from within VSCode by pressing From c953b6f91ba08999cbef0a979df82d031955d4fe Mon Sep 17 00:00:00 2001 From: jjspace <8007967+jjspace@users.noreply.github.com> Date: Fri, 15 Nov 2024 15:58:10 -0500 Subject: [PATCH 081/175] cleanup and adjustments from pr comments --- Apps/Sandcastle/gallery/iTwin Demo.html | 4 +- packages/engine/Source/Core/ITwin.js | 111 ++++++++++++------ .../Source/Scene/createIModel3DTileset.js | 41 ++++--- 3 files changed, 97 insertions(+), 59 deletions(-) diff --git a/Apps/Sandcastle/gallery/iTwin Demo.html b/Apps/Sandcastle/gallery/iTwin Demo.html index eeea23b17f51..a26bc50f5dbb 100644 --- a/Apps/Sandcastle/gallery/iTwin Demo.html +++ b/Apps/Sandcastle/gallery/iTwin Demo.html @@ -32,9 +32,8 @@ //Sandcastle_Begin const serviceResponse = await fetch("http://localhost:3000/service"); const { token } = await serviceResponse.json(); - const accessToken = `Bearer ${token}`; - Cesium.ITwin.defaultAccessToken = accessToken; + Cesium.ITwin.defaultAccessToken = token; // this is the iModel in the "Hello iTwinCesium" iTwin that we should all have access to // https://developer.bentley.com/my-itwins/b4a30036-0456-49ea-a439-3fcd9365e24e/home/ // const imodelId = "2852c3d7-00c3-4b5d-a0ce-82bbde4f061e"; @@ -135,7 +134,6 @@ const exportId = await Cesium.ITwin.createExportForModelId( imodelId, changesetId, - accessToken, ); statusOutput.innerText = "Creating Tileset from export"; tileset = await Cesium.createIModel3DTileset.fromExportId(exportId); diff --git a/packages/engine/Source/Core/ITwin.js b/packages/engine/Source/Core/ITwin.js index 51a89e15668d..249acd76ef88 100644 --- a/packages/engine/Source/Core/ITwin.js +++ b/packages/engine/Source/Core/ITwin.js @@ -1,30 +1,9 @@ +import Check from "./Check.js"; import defined from "./defined.js"; import DeveloperError from "./DeveloperError.js"; import Resource from "./Resource.js"; import RuntimeError from "./RuntimeError.js"; -/** - * @enum {string} - */ -const ExportStatus = Object.freeze({ - NotStarted: "NotStarted", - InProgress: "InProgress", - Complete: "Complete", - Invalid: "Invalid", -}); - -/** - * Type of an export currently, only GLTF and 3DFT are documented - * The CESIUM option is what we were told to use with Sandcastle - * I've also seen the IMODEL one but don't know where it's from - * @enum {string} - */ -const ExportType = Object.freeze({ - IMODEL: "IMODEL", - CESIUM: "CESIUM", - "3DTILES": "3DTILES", -}); - /** * @typedef {Object} GeometryOptions * @property {boolean} includeLines @@ -47,7 +26,7 @@ const ExportType = Object.freeze({ * @typedef {Object} StartExport * @property {string} iModelId * @property {string} changesetId - * @property {ExportType} exportType Type of mesh to create. Currently, only GLTF and 3DFT are supported and undocumented CESIUM option + * @property {ITwin.ExportType} exportType Type of mesh to create. Currently, only GLTF and 3DFT are supported and undocumented CESIUM option * @property {GeometryOptions} geometryOptions * @property {ViewDefinitionFilter} viewDefinitionFilter */ @@ -61,7 +40,7 @@ const ExportType = Object.freeze({ * @typedef {Object} Export * @property {string} id * @property {string} displayName - * @property {ExportStatus} status + * @property {ITwin.ExportStatus} status * @property {StartExport} request * @property {{mesh: Link}} _links */ @@ -74,13 +53,32 @@ const ExportType = Object.freeze({ /** * Default settings for accessing the iTwin platform. * - * @experimental + * @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy. * * @see createIModel3DTileset * @namespace ITwin */ const ITwin = {}; +/** + * @enum {string} + */ +ITwin.ExportStatus = Object.freeze({ + NotStarted: "NotStarted", + InProgress: "InProgress", + Complete: "Complete", + Invalid: "Invalid", +}); + +/** + * @enum {string} + */ +ITwin.ExportType = Object.freeze({ + IMODEL: "IMODEL", + CESIUM: "CESIUM", + "3DTILES": "3DTILES", +}); + /** * Gets or sets the default iTwin access token. * @@ -92,7 +90,7 @@ const ITwin = {}; * `itwin-platform` if we want to use the iModel shares ourselves GET /imodels/{id}/shares * Seems the `itwin-platform` scope should apply to everything but the docs are a little unclear * - * @experimental + * @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy. * * @type {string|undefined} */ @@ -101,7 +99,7 @@ ITwin.defaultAccessToken = undefined; /** * Gets or sets the default iTwin API endpoint. * - * @experimental + * @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy. * * @type {string|Resource} * @default https://api.bentley.com @@ -113,17 +111,25 @@ ITwin.apiEndpoint = new Resource({ /** * Get the export object for the specified export id * - * @experimental + * @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy. * * @param {string} exportId + * + * @throws {RuntimeError} Unauthorized, bad token, wrong scopes or headers bad. + * @throws {RuntimeError} Requested export is not available + * @throws {RuntimeError} Too many requests + * @throws {RuntimeError} Unknown request failure */ ITwin.getExport = async function (exportId) { + //>>includeStart('debug', pragmas.debug); + Check.typeOf.string("exportId", exportId); if (!defined(ITwin.defaultAccessToken)) { throw new DeveloperError("Must set ITwin.defaultAccessToken first"); } + //>>includeEnd('debug') const headers = { - Authorization: ITwin.defaultAccessToken, + Authorization: `Bearer ${ITwin.defaultAccessToken}`, Accept: "application/vnd.bentley.itwin-platform.v1+json", }; @@ -155,27 +161,47 @@ ITwin.getExport = async function (exportId) { /** * Get the list of exports for the given iModel + changeset * - * @experimental + * @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy. * * @param {string} iModelId * @param {string} [changesetId] + * + * @throws {RuntimeError} Unauthorized, bad token, wrong scopes or headers bad. + * @throws {RuntimeError} Not allowed, forbidden + * @throws {RuntimeError} Unprocessable Entity + * @throws {RuntimeError} Too many requests + * @throws {RuntimeError} Unknown request failure */ ITwin.getExports = async function (iModelId, changesetId) { + //>>includeStart('debug', pragmas.debug); + Check.typeOf.string("iModelId", iModelId); + if (defined(changesetId)) { + Check.typeOf.string("changesetId", changesetId); + } if (!defined(ITwin.defaultAccessToken)) { throw new DeveloperError("Must set ITwin.defaultAccessToken first"); } + //>>includeEnd('debug') const headers = { - Authorization: ITwin.defaultAccessToken, + Authorization: `Bearer ${ITwin.defaultAccessToken}`, Accept: "application/vnd.bentley.itwin-platform.v1+json", Prefer: "return=representation", // or return=minimal (the default) }; // obtain export for specified export id // TODO: if we do include the clientVersion what should it be set to? can we sync it with the package.json? - let url = `${ITwin.apiEndpoint}mesh-export/?iModelId=${iModelId}&exportType=${ExportType["3DTILES"]}&$top=1&client=CesiumJS&clientVersion=1.123`; + const url = new URL(`${ITwin.apiEndpoint}mesh-export`); + url.searchParams.set("iModelId", iModelId); if (defined(changesetId) && changesetId !== "") { - url += `&changesetId=${changesetId}`; + url.searchParams.set("changesetId", changesetId); + } + url.searchParams.set("exportType", ITwin.ExportType["3DTILES"]); + url.searchParams.set("$top", "1"); + url.searchParams.set("client", "CesiumJS"); + /* global CESIUM_VERSION */ + if (typeof CESIUM_VERSION !== "undefined") { + url.searchParams.set("clientVersion", CESIUM_VERSION); } const response = await fetch(url, { headers }); @@ -210,29 +236,41 @@ ITwin.getExports = async function (iModelId, changesetId) { * and will be the desired way of interacting with iModels through exports. This function is here * just while we continue testing during the PR process. * - * @experimental + * @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy. * * @param {string} iModelId * @param {string} [changesetId] + * + * @throws {RuntimeError} Unauthorized, bad token, wrong scopes or headers bad. + * @throws {RuntimeError} Not allowed, forbidden + * @throws {RuntimeError} Unprocessable: Cannot create export job + * @throws {RuntimeError} Too many requests + * @throws {RuntimeError} Unknown request failure */ ITwin.createExportForModelId = async function (iModelId, changesetId) { + //>>includeStart('debug', pragmas.debug); + Check.typeOf.string("iModelId", iModelId); + if (defined(changesetId)) { + Check.typeOf.string("changesetId", changesetId); + } if (!defined(ITwin.defaultAccessToken)) { throw new DeveloperError("Must set ITwin.defaultAccessToken first"); } + //>>includeEnd('debug') changesetId = changesetId ?? ""; const requestOptions = { method: "POST", headers: { - Authorization: ITwin.defaultAccessToken, + Authorization: `Bearer ${ITwin.defaultAccessToken}`, Accept: "application/vnd.bentley.itwin-platform.v1+json", "Content-Type": "application/json", }, body: JSON.stringify({ iModelId, changesetId, - exportType: ExportType["3DTILES"], + exportType: ITwin.ExportType["3DTILES"], }), }; @@ -273,4 +311,3 @@ ITwin.createExportForModelId = async function (iModelId, changesetId) { }; export default ITwin; -export { ExportStatus, ExportType }; diff --git a/packages/engine/Source/Scene/createIModel3DTileset.js b/packages/engine/Source/Scene/createIModel3DTileset.js index 9e5908bee402..f4ebb904318b 100644 --- a/packages/engine/Source/Scene/createIModel3DTileset.js +++ b/packages/engine/Source/Scene/createIModel3DTileset.js @@ -1,9 +1,9 @@ import Cesium3DTileset from "./Cesium3DTileset.js"; import defined from "../Core/defined.js"; import Resource from "../Core/Resource.js"; -import ITwin, { ExportStatus, ExportType } from "../Core/ITwin.js"; -import DeveloperError from "../Core/DeveloperError.js"; +import ITwin from "../Core/ITwin.js"; import RuntimeError from "../Core/RuntimeError.js"; +import Check from "../Core/Check.js"; function delay(ms) { return new Promise((res) => setTimeout(res, ms)); @@ -14,18 +14,20 @@ function delay(ms) { * @param {Cesium3DTileset.ConstructorOptions} [options] */ async function loadExport(exportObj, options) { + //>>includeStart('debug', pragmas.debug); + Check.defined("exportObj", exportObj); + //>>includeEnd('debug') + let status = exportObj.status; - if (exportObj.request.exportType !== ExportType["3DTILES"]) { - // This is an undocumented value but I think it's the only one we want to load - // TODO: should we even be checking this? - throw new Error(`Wrong export type ${exportObj.request.exportType}`); + if (exportObj.request.exportType !== ITwin.ExportType["3DTILES"]) { + throw new RuntimeError(`Wrong export type ${exportObj.request.exportType}`); } const timeoutAfter = 300000; const start = Date.now(); // wait until the export is complete - while (status !== ExportStatus.Complete) { + while (status !== ITwin.ExportStatus.Complete) { await delay(5000); exportObj = (await ITwin.getExport(exportObj.id)).export; status = exportObj.status; @@ -36,11 +38,11 @@ async function loadExport(exportObj, options) { } } + // Convert the link to the tileset url while preserving the search paramaters // This link is only valid 1 hour - let tilesetUrl = exportObj._links.mesh.href; - const splitStr = tilesetUrl.split("?"); - // is there a cleaner way to do this? - tilesetUrl = `${splitStr[0]}/tileset.json?${splitStr[1]}`; + const baseUrl = new URL(exportObj._links.mesh.href); + baseUrl.pathname = `${baseUrl.pathname}/tileset.json`; + const tilesetUrl = baseUrl.toString(); const resource = new Resource({ url: tilesetUrl, @@ -55,22 +57,19 @@ const createIModel3DTileset = {}; * Creates a {@link Cesium3DTileset} instance for the Google Photorealistic 3D Tiles tileset. * * @function - * @experimental + * @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy. * * @param {string} exportId * @param {Cesium3DTileset.ConstructorOptions} [options] An object describing initialization options. * @returns {Promise} * - * @see ITwin + * @throws {RuntimeError} Wrong export type + * @throws {RuntimeError} Export did not complete in time. * * @example * TODO: example after API finalized */ createIModel3DTileset.fromExportId = async function (exportId, options) { - if (!defined(ITwin.defaultAccessToken)) { - throw new DeveloperError("Must set ITwin.defaultAccessToken first"); - } - options = options ?? {}; const result = await ITwin.getExport(exportId); @@ -91,11 +90,14 @@ createIModel3DTileset.fromExportId = async function (exportId, options) { * @example * TODO: example after API finalized * - * @experimental + * @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy. * * @param {string} iModelId * @param {string} changesetId * @param {Cesium3DTileset.ConstructorOptions} options + * + * @throws {RuntimeError} Wrong export type + * @throws {RuntimeError} Export did not complete in time. */ createIModel3DTileset.fromModelId = async function ( iModelId, @@ -104,7 +106,8 @@ createIModel3DTileset.fromModelId = async function ( ) { const { exports } = await ITwin.getExports(iModelId, changesetId); const cesiumExport = exports.find( - (exportObj) => exportObj.request?.exportType === ExportType["3DTILES"], + (exportObj) => + exportObj.request?.exportType === ITwin.ExportType["3DTILES"], ); if (!defined(cesiumExport)) { return; From 46bd2f004681ff700989c309477e8dfff4852d19 Mon Sep 17 00:00:00 2001 From: jjspace <8007967+jjspace@users.noreply.github.com> Date: Fri, 15 Nov 2024 17:03:00 -0500 Subject: [PATCH 082/175] rename namespaces --- Apps/Sandcastle/gallery/iTwin Demo.html | 8 ++-- .../Core/{ITwin.js => ITwinPlatform.js} | 48 +++++++++---------- ...{createIModel3DTileset.js => ITwinData.js} | 26 +++++----- 3 files changed, 41 insertions(+), 41 deletions(-) rename packages/engine/Source/Core/{ITwin.js => ITwinPlatform.js} (86%) rename packages/engine/Source/Scene/{createIModel3DTileset.js => ITwinData.js} (81%) diff --git a/Apps/Sandcastle/gallery/iTwin Demo.html b/Apps/Sandcastle/gallery/iTwin Demo.html index a26bc50f5dbb..dc87286bcd24 100644 --- a/Apps/Sandcastle/gallery/iTwin Demo.html +++ b/Apps/Sandcastle/gallery/iTwin Demo.html @@ -33,7 +33,7 @@ const serviceResponse = await fetch("http://localhost:3000/service"); const { token } = await serviceResponse.json(); - Cesium.ITwin.defaultAccessToken = token; + Cesium.ITwinPlatform.defaultAccessToken = token; // this is the iModel in the "Hello iTwinCesium" iTwin that we should all have access to // https://developer.bentley.com/my-itwins/b4a30036-0456-49ea-a439-3fcd9365e24e/home/ // const imodelId = "2852c3d7-00c3-4b5d-a0ce-82bbde4f061e"; @@ -123,7 +123,7 @@ statusOutput.innerText = "Creating Tileset"; - let tileset = await Cesium.createIModel3DTileset.fromModelId( + let tileset = await Cesium.ITwinData.createTilesetFromModelId( imodelId, changesetId, ); @@ -131,12 +131,12 @@ // TODO: this is temporary, we should not have to call the Start Export route ever after // auto generation is set up statusOutput.innerText = "Starting export"; - const exportId = await Cesium.ITwin.createExportForModelId( + const exportId = await Cesium.ITwinPlatform.createExportForModelId( imodelId, changesetId, ); statusOutput.innerText = "Creating Tileset from export"; - tileset = await Cesium.createIModel3DTileset.fromExportId(exportId); + tileset = await Cesium.ITwinData.createTilesetFromExportId(exportId); } scene.primitives.add(tileset); diff --git a/packages/engine/Source/Core/ITwin.js b/packages/engine/Source/Core/ITwinPlatform.js similarity index 86% rename from packages/engine/Source/Core/ITwin.js rename to packages/engine/Source/Core/ITwinPlatform.js index 249acd76ef88..0131e8efd5ee 100644 --- a/packages/engine/Source/Core/ITwin.js +++ b/packages/engine/Source/Core/ITwinPlatform.js @@ -26,7 +26,7 @@ import RuntimeError from "./RuntimeError.js"; * @typedef {Object} StartExport * @property {string} iModelId * @property {string} changesetId - * @property {ITwin.ExportType} exportType Type of mesh to create. Currently, only GLTF and 3DFT are supported and undocumented CESIUM option + * @property {ITwinPlatform.ExportType} exportType Type of mesh to create. Currently, only GLTF and 3DFT are supported and undocumented CESIUM option * @property {GeometryOptions} geometryOptions * @property {ViewDefinitionFilter} viewDefinitionFilter */ @@ -40,7 +40,7 @@ import RuntimeError from "./RuntimeError.js"; * @typedef {Object} Export * @property {string} id * @property {string} displayName - * @property {ITwin.ExportStatus} status + * @property {ITwinPlatform.ExportStatus} status * @property {StartExport} request * @property {{mesh: Link}} _links */ @@ -55,15 +55,15 @@ import RuntimeError from "./RuntimeError.js"; * * @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy. * - * @see createIModel3DTileset - * @namespace ITwin + * @see ITwinData for ways to import data + * @namespace ITwinPlatform */ -const ITwin = {}; +const ITwinPlatform = {}; /** * @enum {string} */ -ITwin.ExportStatus = Object.freeze({ +ITwinPlatform.ExportStatus = Object.freeze({ NotStarted: "NotStarted", InProgress: "InProgress", Complete: "Complete", @@ -73,7 +73,7 @@ ITwin.ExportStatus = Object.freeze({ /** * @enum {string} */ -ITwin.ExportType = Object.freeze({ +ITwinPlatform.ExportType = Object.freeze({ IMODEL: "IMODEL", CESIUM: "CESIUM", "3DTILES": "3DTILES", @@ -94,7 +94,7 @@ ITwin.ExportType = Object.freeze({ * * @type {string|undefined} */ -ITwin.defaultAccessToken = undefined; +ITwinPlatform.defaultAccessToken = undefined; /** * Gets or sets the default iTwin API endpoint. @@ -104,7 +104,7 @@ ITwin.defaultAccessToken = undefined; * @type {string|Resource} * @default https://api.bentley.com */ -ITwin.apiEndpoint = new Resource({ +ITwinPlatform.apiEndpoint = new Resource({ url: "https://api.bentley.com", }); @@ -120,21 +120,21 @@ ITwin.apiEndpoint = new Resource({ * @throws {RuntimeError} Too many requests * @throws {RuntimeError} Unknown request failure */ -ITwin.getExport = async function (exportId) { +ITwinPlatform.getExport = async function (exportId) { //>>includeStart('debug', pragmas.debug); Check.typeOf.string("exportId", exportId); - if (!defined(ITwin.defaultAccessToken)) { + if (!defined(ITwinPlatform.defaultAccessToken)) { throw new DeveloperError("Must set ITwin.defaultAccessToken first"); } //>>includeEnd('debug') const headers = { - Authorization: `Bearer ${ITwin.defaultAccessToken}`, + Authorization: `Bearer ${ITwinPlatform.defaultAccessToken}`, Accept: "application/vnd.bentley.itwin-platform.v1+json", }; // obtain export for specified export id - const url = `${ITwin.apiEndpoint}mesh-export/${exportId}`; + const url = `${ITwinPlatform.apiEndpoint}mesh-export/${exportId}`; // TODO: this request is _really_ slow, like 7 whole second alone for me // Arun said this was kinda normal but to keep track of the `x-correlation-id` of any that take EXTRA long @@ -172,31 +172,31 @@ ITwin.getExport = async function (exportId) { * @throws {RuntimeError} Too many requests * @throws {RuntimeError} Unknown request failure */ -ITwin.getExports = async function (iModelId, changesetId) { +ITwinPlatform.getExports = async function (iModelId, changesetId) { //>>includeStart('debug', pragmas.debug); Check.typeOf.string("iModelId", iModelId); if (defined(changesetId)) { Check.typeOf.string("changesetId", changesetId); } - if (!defined(ITwin.defaultAccessToken)) { + if (!defined(ITwinPlatform.defaultAccessToken)) { throw new DeveloperError("Must set ITwin.defaultAccessToken first"); } //>>includeEnd('debug') const headers = { - Authorization: `Bearer ${ITwin.defaultAccessToken}`, + Authorization: `Bearer ${ITwinPlatform.defaultAccessToken}`, Accept: "application/vnd.bentley.itwin-platform.v1+json", Prefer: "return=representation", // or return=minimal (the default) }; // obtain export for specified export id // TODO: if we do include the clientVersion what should it be set to? can we sync it with the package.json? - const url = new URL(`${ITwin.apiEndpoint}mesh-export`); + const url = new URL(`${ITwinPlatform.apiEndpoint}mesh-export`); url.searchParams.set("iModelId", iModelId); if (defined(changesetId) && changesetId !== "") { url.searchParams.set("changesetId", changesetId); } - url.searchParams.set("exportType", ITwin.ExportType["3DTILES"]); + url.searchParams.set("exportType", ITwinPlatform.ExportType["3DTILES"]); url.searchParams.set("$top", "1"); url.searchParams.set("client", "CesiumJS"); /* global CESIUM_VERSION */ @@ -247,13 +247,13 @@ ITwin.getExports = async function (iModelId, changesetId) { * @throws {RuntimeError} Too many requests * @throws {RuntimeError} Unknown request failure */ -ITwin.createExportForModelId = async function (iModelId, changesetId) { +ITwinPlatform.createExportForModelId = async function (iModelId, changesetId) { //>>includeStart('debug', pragmas.debug); Check.typeOf.string("iModelId", iModelId); if (defined(changesetId)) { Check.typeOf.string("changesetId", changesetId); } - if (!defined(ITwin.defaultAccessToken)) { + if (!defined(ITwinPlatform.defaultAccessToken)) { throw new DeveloperError("Must set ITwin.defaultAccessToken first"); } //>>includeEnd('debug') @@ -263,20 +263,20 @@ ITwin.createExportForModelId = async function (iModelId, changesetId) { const requestOptions = { method: "POST", headers: { - Authorization: `Bearer ${ITwin.defaultAccessToken}`, + Authorization: `Bearer ${ITwinPlatform.defaultAccessToken}`, Accept: "application/vnd.bentley.itwin-platform.v1+json", "Content-Type": "application/json", }, body: JSON.stringify({ iModelId, changesetId, - exportType: ITwin.ExportType["3DTILES"], + exportType: ITwinPlatform.ExportType["3DTILES"], }), }; // initiate mesh export const response = await fetch( - `${ITwin.apiEndpoint}mesh-export/`, + `${ITwinPlatform.apiEndpoint}mesh-export/`, requestOptions, ); @@ -310,4 +310,4 @@ ITwin.createExportForModelId = async function (iModelId, changesetId) { return result.export.id; }; -export default ITwin; +export default ITwinPlatform; diff --git a/packages/engine/Source/Scene/createIModel3DTileset.js b/packages/engine/Source/Scene/ITwinData.js similarity index 81% rename from packages/engine/Source/Scene/createIModel3DTileset.js rename to packages/engine/Source/Scene/ITwinData.js index f4ebb904318b..e64307798e99 100644 --- a/packages/engine/Source/Scene/createIModel3DTileset.js +++ b/packages/engine/Source/Scene/ITwinData.js @@ -1,7 +1,7 @@ import Cesium3DTileset from "./Cesium3DTileset.js"; import defined from "../Core/defined.js"; import Resource from "../Core/Resource.js"; -import ITwin from "../Core/ITwin.js"; +import ITwinPlatform from "../Core/ITwinPlatform.js"; import RuntimeError from "../Core/RuntimeError.js"; import Check from "../Core/Check.js"; @@ -20,16 +20,16 @@ async function loadExport(exportObj, options) { let status = exportObj.status; - if (exportObj.request.exportType !== ITwin.ExportType["3DTILES"]) { + if (exportObj.request.exportType !== ITwinPlatform.ExportType["3DTILES"]) { throw new RuntimeError(`Wrong export type ${exportObj.request.exportType}`); } const timeoutAfter = 300000; const start = Date.now(); // wait until the export is complete - while (status !== ITwin.ExportStatus.Complete) { + while (status !== ITwinPlatform.ExportStatus.Complete) { await delay(5000); - exportObj = (await ITwin.getExport(exportObj.id)).export; + exportObj = (await ITwinPlatform.getExport(exportObj.id)).export; status = exportObj.status; console.log(`Export is ${status}`); @@ -51,7 +51,7 @@ async function loadExport(exportObj, options) { return Cesium3DTileset.fromUrl(resource, options); } -const createIModel3DTileset = {}; +const ITwinData = {}; /** * Creates a {@link Cesium3DTileset} instance for the Google Photorealistic 3D Tiles tileset. @@ -69,10 +69,10 @@ const createIModel3DTileset = {}; * @example * TODO: example after API finalized */ -createIModel3DTileset.fromExportId = async function (exportId, options) { +ITwinData.createTilesetFromExportId = async function (exportId, options) { options = options ?? {}; - const result = await ITwin.getExport(exportId); + const result = await ITwinPlatform.getExport(exportId); const tileset = await loadExport(result.export, options); return tileset; }; @@ -81,10 +81,10 @@ createIModel3DTileset.fromExportId = async function (exportId, options) { * Check the exports for the given iModel + changeset combination for any that * have the desired CESIUM type and returns the first one that matches as a new tileset. * - * If there is not a CESIUM export you can create it using {@link ITwin.createExportForModelId} + * If there is not a CESIUM export you can create it using {@link ITwinPlatform.createExportForModelId} * * This function assumes one export per type per "iModel id + changeset id". If you need to create - * multiple exports per "iModel id + changeset id" you should switch to using {@link createIModel3DTileset} + * multiple exports per "iModel id + changeset id" you should switch to using {@link ITwinData} * with the export id directly * * @example @@ -99,15 +99,15 @@ createIModel3DTileset.fromExportId = async function (exportId, options) { * @throws {RuntimeError} Wrong export type * @throws {RuntimeError} Export did not complete in time. */ -createIModel3DTileset.fromModelId = async function ( +ITwinData.createTilesetFromModelId = async function ( iModelId, changesetId, options, ) { - const { exports } = await ITwin.getExports(iModelId, changesetId); + const { exports } = await ITwinPlatform.getExports(iModelId, changesetId); const cesiumExport = exports.find( (exportObj) => - exportObj.request?.exportType === ITwin.ExportType["3DTILES"], + exportObj.request?.exportType === ITwinPlatform.ExportType["3DTILES"], ); if (!defined(cesiumExport)) { return; @@ -115,4 +115,4 @@ createIModel3DTileset.fromModelId = async function ( return loadExport(cesiumExport, options); }; -export default createIModel3DTileset; +export default ITwinData; From 13ac596e9ce7fca3c49f0a1c82c410ab56a47ade Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Sun, 17 Nov 2024 14:14:55 +0100 Subject: [PATCH 083/175] No JSDoc link to private classes --- packages/engine/Source/Scene/PickedMetadataInfo.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/engine/Source/Scene/PickedMetadataInfo.js b/packages/engine/Source/Scene/PickedMetadataInfo.js index 40a727a8de43..d58fa33e0451 100644 --- a/packages/engine/Source/Scene/PickedMetadataInfo.js +++ b/packages/engine/Source/Scene/PickedMetadataInfo.js @@ -45,11 +45,12 @@ function PickedMetadataInfo( this.classProperty = classProperty; /** - * The metadata property that is described by this structure, as - * obtained from the property texture or property attribute of the - * `StructuralMetadata` that matches the class name and property name. + * The `PropertyTextureProperty` or `PropertyAttributeProperty` that + * is described by this structure, as obtained from the property texture + * or property attribute of the `StructuralMetadata` that matches the + * class name and property name. * - * @type {PropertyTextureProperty|PropertyAttributeProperty} + * @type {object} */ this.metadataProperty = metadataProperty; } From 471dc32571094a495d0a9b06cfdfd296d72af6d2 Mon Sep 17 00:00:00 2001 From: juicysteak Date: Tue, 19 Nov 2024 08:35:49 +0800 Subject: [PATCH 084/175] Check if the input is a number Co-authored-by: Gabby Getz --- packages/engine/Source/DataSources/SampledProperty.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/engine/Source/DataSources/SampledProperty.js b/packages/engine/Source/DataSources/SampledProperty.js index ef6b6ee60df2..dc9c2f3acb8b 100644 --- a/packages/engine/Source/DataSources/SampledProperty.js +++ b/packages/engine/Source/DataSources/SampledProperty.js @@ -682,7 +682,7 @@ SampledProperty.prototype.addSamples = function ( */ SampledProperty.prototype.getSample = function (index) { //>>includeStart('debug', pragmas.debug); - Check.defined("index", index); + Check.typeOf.number("index", index); //>>includeEnd('debug'); const times = this._times; From fb6828a0214ff8deb22a6b77872fc46496a21901 Mon Sep 17 00:00:00 2001 From: juicysteak Date: Tue, 19 Nov 2024 08:36:59 +0800 Subject: [PATCH 085/175] check is undefined by `defined` func Co-authored-by: Gabby Getz --- packages/engine/Source/DataSources/SampledProperty.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/engine/Source/DataSources/SampledProperty.js b/packages/engine/Source/DataSources/SampledProperty.js index dc9c2f3acb8b..4aecf230ca50 100644 --- a/packages/engine/Source/DataSources/SampledProperty.js +++ b/packages/engine/Source/DataSources/SampledProperty.js @@ -687,7 +687,7 @@ SampledProperty.prototype.getSample = function (index) { const times = this._times; const len = times.length; - if (!len) { + if (!defined(len)) { return undefined; } From 8ee331b68c36124971276c8c7f4fed3b21546b96 Mon Sep 17 00:00:00 2001 From: juicysteak Date: Tue, 19 Nov 2024 08:37:44 +0800 Subject: [PATCH 086/175] improve the doc of `getSample` Co-authored-by: Gabby Getz --- packages/engine/Source/DataSources/SampledProperty.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/engine/Source/DataSources/SampledProperty.js b/packages/engine/Source/DataSources/SampledProperty.js index 4aecf230ca50..dde0676d987e 100644 --- a/packages/engine/Source/DataSources/SampledProperty.js +++ b/packages/engine/Source/DataSources/SampledProperty.js @@ -675,7 +675,7 @@ SampledProperty.prototype.addSamples = function ( }; /** - * Gets the time of the sample. Negative for backward. + * Retrieves the time of the provided sample associated with the index. A negative index accesses the list of samples in reverse order. * * @param {number} index The index of samples list. * @returns {JulianDate | undefined} The JulianDate time of the sample, or undefined if failed. From 4599ab0b2269d6a10d06af4cd1ebed899fb83262 Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Tue, 19 Nov 2024 16:40:07 +0100 Subject: [PATCH 087/175] More specific types for picked metadata --- .../engine/Source/Scene/MetadataPicking.js | 18 ++++++++++++----- packages/engine/Source/Scene/MetadataType.js | 20 +++++++++++++++++++ .../Gpm/GltfMeshPrimitiveGpmLoader.js | 2 +- packages/engine/Source/Scene/Picking.js | 7 ++++++- packages/engine/Source/Scene/Scene.js | 3 ++- 5 files changed, 42 insertions(+), 8 deletions(-) diff --git a/packages/engine/Source/Scene/MetadataPicking.js b/packages/engine/Source/Scene/MetadataPicking.js index 5364824c64bd..1ecbb639a57a 100644 --- a/packages/engine/Source/Scene/MetadataPicking.js +++ b/packages/engine/Source/Scene/MetadataPicking.js @@ -244,7 +244,7 @@ MetadataPicking.decodeRawMetadataValues = function ( * * @param {string} type The `ClassProperty` type * @param {number|bigint|number[]|bigint[]|undefined} value The input value - * @returns {any} The object representation + * @returns {undefined|number|bigint|string|boolean|Cartesian2|Cartesian3|Cartesian4|Matrix2|Matrix3|Matrix4} The object representation * @throws RuntimeError If the type is not a valid `MetadataType` */ MetadataPicking.convertToObjectType = function (type, value) { @@ -285,8 +285,8 @@ MetadataPicking.convertToObjectType = function (type, value) { * For other types, the value is returned directly * * @param {string} type The `ClassProperty` type - * @param {any} value The input value - * @returns {any} The array representation + * @param {undefined|number|bigint|string|boolean|Cartesian2|Cartesian3|Cartesian4|Matrix2|Matrix3|Matrix4} value The input value + * @returns {undefined|number|bigint|string|boolean|number[]} The array representation * @throws RuntimeError If the type is not a valid `MetadataType` */ MetadataPicking.convertFromObjectType = function (type, value) { @@ -327,10 +327,10 @@ MetadataPicking.convertFromObjectType = function (type, value) { * types into object types like `CartesianN`. * * @param {MetadataClassProperty} classProperty The `MetadataClassProperty` - * @param {MetadataClassProperty} metadataProperty The + * @param {object} metadataProperty The * `PropertyTextureProperty` or `PropertyAttributeProperty` * @param {Uint8Array} rawPixelValues The raw values - * @returns {any} The value + * @returns {MetadataValue} The value * @throws RuntimeError If the class property has an invalid type * or component type * @throws RangeError If the given pixel values do not have sufficient @@ -349,6 +349,14 @@ MetadataPicking.decodeMetadataValues = function ( ); if (metadataProperty.hasValueTransform) { + // In the MetadataClassProperty, these offset/scale are always in + // their array-based form (e.g. a number[3] for `VEC3`). But for + // the PropertyTextureProperty and PropertyAttributeProperty, + // the type of the offset/scale is defined to be + // number|Cartesian2|Cartesian3|Cartesian4|Matrix2|Matrix3|Matrix4 + // So these types are converted into their array-based form here, before + // applying them with `MetadataClassProperty.valueTransformInPlace` + const offset = MetadataPicking.convertFromObjectType( classProperty.type, metadataProperty.offset, diff --git a/packages/engine/Source/Scene/MetadataType.js b/packages/engine/Source/Scene/MetadataType.js index 9bb0a2e8c7d5..c00e91f2cb1e 100644 --- a/packages/engine/Source/Scene/MetadataType.js +++ b/packages/engine/Source/Scene/MetadataType.js @@ -7,6 +7,26 @@ import Matrix2 from "../Core/Matrix2.js"; import Matrix3 from "../Core/Matrix3.js"; import Matrix4 from "../Core/Matrix4.js"; +/** + * An instance of a metadata value.
+ *
+ * This can be one of the following types: + *
    + *
  • number for type SCALAR and numeric component types except for INT64 or UINT64
  • + *
  • bigint for type SCALAR and component type INT64 or UINT64
  • + *
  • string for type STRING or ENUM
  • + *
  • boolean for type BOOLEAN
  • + *
  • Cartesian2 for type VEC2
  • + *
  • Cartesian3 for type VEC3
  • + *
  • Cartesian4 for type VEC4
  • + *
  • Matrix2 for type MAT2
  • + *
  • Matrix3 for type MAT3
  • + *
  • Matrix4 for type MAT4
  • + *
  • Arrays of these types when the metadata value is an array
  • + *
+ * @typedef {(number|bigint|string|boolean|Cartesian2|Cartesian3|Cartesian4|Matrix2|Matrix3|Matrix4|number[]|bigint[]|string[]|boolean[]|Cartesian2[]|Cartesian3[]|Cartesian4[]|Matrix2[]|Matrix3[]|Matrix4[])} MetadataValue + */ + /** * An enum of metadata types. These metadata types are containers containing * one or more components of type {@link MetadataComponentType} diff --git a/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfMeshPrimitiveGpmLoader.js b/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfMeshPrimitiveGpmLoader.js index b3ef4f258ed7..944d99ab23b7 100644 --- a/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfMeshPrimitiveGpmLoader.js +++ b/packages/engine/Source/Scene/Model/Extensions/Gpm/GltfMeshPrimitiveGpmLoader.js @@ -234,7 +234,7 @@ GltfMeshPrimitiveGpmLoader.ppeTexturesMetadataSchemaCache = new Map(); * Create the JSON description of a metadata class that treats * the given PPE texture as a property texture property. * - * @param {any} ppeTexture - The PPE texture + * @param {PpeTexture} ppeTexture - The PPE texture * @param {number} index - The index of the texture in the extension * @returns The class JSON */ diff --git a/packages/engine/Source/Scene/Picking.js b/packages/engine/Source/Scene/Picking.js index 8ded3ccbe400..c31490f9851c 100644 --- a/packages/engine/Source/Scene/Picking.js +++ b/packages/engine/Source/Scene/Picking.js @@ -426,9 +426,14 @@ Picking.prototype.pickVoxelCoordinate = function ( * - For `VEC3`, the return type will be a `Cartesian3` * - For `VEC4`, the return type will be a `Cartesian4` * + * Future implementations may additionally return `string`- or + * `boolean` types, and `MATn` values as `MatrixN` objects, + * and arrays of the respective types. + * * @param {Cartesian2} windowPosition Window coordinates to perform picking on. * @param {PickedMetadataInfo} pickedMetadataInfo Information about the picked metadata. - * @returns {any} The metadata values + * @returns {MetadataValue|undefined} The metadata value, or `undefined` + * when no matching metadata value could be picked at the given position * * @private */ diff --git a/packages/engine/Source/Scene/Scene.js b/packages/engine/Source/Scene/Scene.js index 0a2543ef7fd1..bb1f092502c4 100644 --- a/packages/engine/Source/Scene/Scene.js +++ b/packages/engine/Source/Scene/Scene.js @@ -4403,7 +4403,8 @@ Scene.prototype.pickVoxel = function (windowPosition, width, height) { * values from * @param {string} propertyName The name of the metadata property to pick * values from - * @returns {any} The metadata value + * @returns {MetadataValue|undefined} The metadata value, or `undefined` when + * to matching metadata was found at the given position * * @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy. */ From 40525b38787381046e8332811de4ef3d31df927f Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Tue, 19 Nov 2024 16:55:49 +0100 Subject: [PATCH 088/175] Fix typo in comment --- packages/engine/Source/Scene/Scene.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/engine/Source/Scene/Scene.js b/packages/engine/Source/Scene/Scene.js index bb1f092502c4..e438fdf87e32 100644 --- a/packages/engine/Source/Scene/Scene.js +++ b/packages/engine/Source/Scene/Scene.js @@ -4404,7 +4404,7 @@ Scene.prototype.pickVoxel = function (windowPosition, width, height) { * @param {string} propertyName The name of the metadata property to pick * values from * @returns {MetadataValue|undefined} The metadata value, or `undefined` when - * to matching metadata was found at the given position + * no matching metadata was found at the given position * * @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy. */ From 68b2137f084001010b766100d441e130cfb2d7d6 Mon Sep 17 00:00:00 2001 From: Jeshurun Hembd Date: Tue, 19 Nov 2024 12:43:46 -0500 Subject: [PATCH 089/175] Minor cleanup --- .../engine/Source/Scene/PostProcessStage.js | 31 +++++++++---------- .../AmbientOcclusionGenerate.glsl | 14 ++++----- 2 files changed, 21 insertions(+), 24 deletions(-) diff --git a/packages/engine/Source/Scene/PostProcessStage.js b/packages/engine/Source/Scene/PostProcessStage.js index 44012ca77b56..454822ca4e8f 100644 --- a/packages/engine/Source/Scene/PostProcessStage.js +++ b/packages/engine/Source/Scene/PostProcessStage.js @@ -94,9 +94,16 @@ import PostProcessStageSampleMode from "./PostProcessStageSampleMode.js"; function PostProcessStage(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); const { + name = createGuid(), fragmentShader, + uniforms, textureScale = 1.0, + forcePowerOfTwo = false, + sampleMode = PostProcessStageSampleMode.NEAREST, pixelFormat = PixelFormat.RGBA, + pixelDatatype = PixelDatatype.UNSIGNED_BYTE, + clearColor = Color.BLACK, + scissorRectangle, } = options; //>>includeStart('debug', pragmas.debug); @@ -113,19 +120,13 @@ function PostProcessStage(options) { //>>includeEnd('debug'); this._fragmentShader = fragmentShader; - this._uniforms = options.uniforms; + this._uniforms = uniforms; this._textureScale = textureScale; - this._forcePowerOfTwo = defaultValue(options.forcePowerOfTwo, false); - this._sampleMode = defaultValue( - options.sampleMode, - PostProcessStageSampleMode.NEAREST, - ); + this._forcePowerOfTwo = forcePowerOfTwo; + this._sampleMode = sampleMode; this._pixelFormat = pixelFormat; - this._pixelDatatype = defaultValue( - options.pixelDatatype, - PixelDatatype.UNSIGNED_BYTE, - ); - this._clearColor = defaultValue(options.clearColor, Color.BLACK); + this._pixelDatatype = pixelDatatype; + this._clearColor = clearColor; this._uniformMap = undefined; this._command = undefined; @@ -143,18 +144,14 @@ function PostProcessStage(options) { const passState = new PassState(); passState.scissorTest = { enabled: true, - rectangle: defined(options.scissorRectangle) - ? BoundingRectangle.clone(options.scissorRectangle) + rectangle: defined(scissorRectangle) + ? BoundingRectangle.clone(scissorRectangle) : new BoundingRectangle(), }; this._passState = passState; this._ready = false; - let name = options.name; - if (!defined(name)) { - name = createGuid(); - } this._name = name; this._logDepthChanged = undefined; diff --git a/packages/engine/Source/Shaders/PostProcessStages/AmbientOcclusionGenerate.glsl b/packages/engine/Source/Shaders/PostProcessStages/AmbientOcclusionGenerate.glsl index 8975807958da..6e6c2e0e0152 100644 --- a/packages/engine/Source/Shaders/PostProcessStages/AmbientOcclusionGenerate.glsl +++ b/packages/engine/Source/Shaders/PostProcessStages/AmbientOcclusionGenerate.glsl @@ -97,25 +97,25 @@ void main(void) break; } - // Compute distance along geometry, for weighting AO contribution + // Compute step vector from output point to sampled point vec3 stepPositionEC = pixelToEye(newCoords); vec3 stepVector = stepPositionEC - positionEC; - float stepLength = length(stepVector); - - // Compute lateral distance from output point, for weight normalization - vec3 inPlaneStepEC = vec3(stepPositionEC.x, stepPositionEC.y, positionEC.z); - vec3 windowVector = inPlaneStepEC - positionEC; - float windowLength = length(windowVector); + // Estimate the angle from the surface normal. float dotVal = clamp(dot(normalEC, normalize(stepVector)), 0.0, 1.0); if (dotVal < bias) { dotVal = 0.0; } + // Weight contribution based on the distance from the output point + float stepLength = length(stepVector); float weight = gaussian(stepLength, samplingRadius); localAO += weight * dotVal; + + // Compute lateral distance from output point, for weight normalization // TODO: This is slow! Better to analytically compute window scales + float windowLength = length(stepPositionEC.xy - positionEC.xy); accumulatedWindowWeights += gaussian(windowLength, samplingRadius); } ao += 24.0 * localAO / accumulatedWindowWeights; From fb385b962909ee12e30b4388caec8c918589655f Mon Sep 17 00:00:00 2001 From: Connor Manning Date: Tue, 19 Nov 2024 13:16:18 -0600 Subject: [PATCH 090/175] Discard hidden points in the fragment shader, rather than moving them in the vertex shader. Fixes #11270. --- .../Scene/Model/PointCloudStylingPipelineStage.js | 3 ++- packages/engine/Source/Shaders/Model/ModelFS.glsl | 7 +++++++ packages/engine/Source/Shaders/Model/ModelVS.glsl | 10 +++++++++- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/packages/engine/Source/Scene/Model/PointCloudStylingPipelineStage.js b/packages/engine/Source/Scene/Model/PointCloudStylingPipelineStage.js index 967e07fcfe98..c89786f88f05 100644 --- a/packages/engine/Source/Scene/Model/PointCloudStylingPipelineStage.js +++ b/packages/engine/Source/Scene/Model/PointCloudStylingPipelineStage.js @@ -333,9 +333,10 @@ function addShaderFunctionsAndDefines(shaderBuilder, shaderFunctionInfo) { shaderBuilder.addDefine( "HAS_POINT_CLOUD_SHOW_STYLE", undefined, - ShaderDestination.VERTEX, + ShaderDestination.BOTH, ); shaderBuilder.addVertexLines(showStyleFunction); + shaderBuilder.addVarying("float", "v_pointCloudShow"); } const pointSizeStyleFunction = shaderFunctionInfo.pointSizeStyleFunction; diff --git a/packages/engine/Source/Shaders/Model/ModelFS.glsl b/packages/engine/Source/Shaders/Model/ModelFS.glsl index 0500c0b9f1ff..a6cb2827f60f 100644 --- a/packages/engine/Source/Shaders/Model/ModelFS.glsl +++ b/packages/engine/Source/Shaders/Model/ModelFS.glsl @@ -28,6 +28,13 @@ SelectedFeature selectedFeature; void main() { + #ifdef HAS_POINT_CLOUD_SHOW_STYLE + if (v_pointCloudShow == 0.0) + { + discard; + } + #endif + #ifdef HAS_MODEL_SPLITTER modelSplitterStage(); #endif diff --git a/packages/engine/Source/Shaders/Model/ModelVS.glsl b/packages/engine/Source/Shaders/Model/ModelVS.glsl index b95b329f59ff..2164da1024bc 100644 --- a/packages/engine/Source/Shaders/Model/ModelVS.glsl +++ b/packages/engine/Source/Shaders/Model/ModelVS.glsl @@ -145,5 +145,13 @@ void main() gl_PointSize *= show; #endif - gl_Position = show * positionClip; + // Important NOT to compute gl_Position = show * positionClip or we hit: + // https://github.com/CesiumGS/cesium/issues/11270 + // + // We will discard points with v_pointCloudShow == 0 in the fragment shader. + gl_Position = positionClip; + + #ifdef HAS_POINT_CLOUD_SHOW_STYLE + v_pointCloudShow = show; + #endif } From 251a67f43da47c718b03b9358fb1c9c95c586930 Mon Sep 17 00:00:00 2001 From: Connor Manning Date: Tue, 19 Nov 2024 14:19:36 -0600 Subject: [PATCH 091/175] Add to contributors list. --- CONTRIBUTORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index cc7913527093..d53965da58ef 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -416,3 +416,4 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for details on how to contribute to Cesiu - [Jérôme Fayot](https://github.com/jfayot) - [Kirn Kim](https://github.com/squrki) - [Emanuele Mastaglia](https://github.com/Masty88) +- [Connor Manning](https://github.com/connormanning) From 7bf4fbed1c011d7ab29fa03abad27b402f512e56 Mon Sep 17 00:00:00 2001 From: Connor Manning Date: Tue, 19 Nov 2024 14:21:29 -0600 Subject: [PATCH 092/175] Add note to changelog. --- CHANGES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.md b/CHANGES.md index 49e2c0056ac6..ade02069a297 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -11,6 +11,7 @@ ##### Fixes :wrench: - Fix label rendering bug in WebGL1 contexts. [#12301](https://github.com/CesiumGS/cesium/pull/12301) +- Fix point cloud filtering performance on certain hardware [#12317](https://github.com/CesiumGS/cesium/pull/12317) #### @cesium/widgets From b511f59965055dc1de363647e8ca078287300e47 Mon Sep 17 00:00:00 2001 From: Mark Dane Date: Tue, 19 Nov 2024 16:24:29 -0500 Subject: [PATCH 093/175] Remove geocoder from sandcastles with non bing/google map data --- Apps/Sandcastle/gallery/Imagery Layers Manipulation.html | 2 +- Apps/Sandcastle/gallery/Imagery Layers Split.html | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Apps/Sandcastle/gallery/Imagery Layers Manipulation.html b/Apps/Sandcastle/gallery/Imagery Layers Manipulation.html index 48a3efc177ab..9b1f026cdbab 100644 --- a/Apps/Sandcastle/gallery/Imagery Layers Manipulation.html +++ b/Apps/Sandcastle/gallery/Imagery Layers Manipulation.html @@ -106,7 +106,7 @@ //Sandcastle_Begin const viewer = new Cesium.Viewer("cesiumContainer", { baseLayerPicker: false, - geocoder: Cesium.IonGeocodeProviderType.BING, + geocoder: false, }); const imageryLayers = viewer.imageryLayers; diff --git a/Apps/Sandcastle/gallery/Imagery Layers Split.html b/Apps/Sandcastle/gallery/Imagery Layers Split.html index 37927116fc29..15375661bb5f 100644 --- a/Apps/Sandcastle/gallery/Imagery Layers Split.html +++ b/Apps/Sandcastle/gallery/Imagery Layers Split.html @@ -58,6 +58,7 @@ ), baseLayerPicker: false, infoBox: false, + geocoder: false, }); const layers = viewer.imageryLayers; From 1757f77a0878d695d80f4f4c2d24172f994adf55 Mon Sep 17 00:00:00 2001 From: jjspace <8007967+jjspace@users.noreply.github.com> Date: Tue, 19 Nov 2024 16:32:03 -0500 Subject: [PATCH 094/175] partial cleanup, remove changeset ids --- Apps/Sandcastle/gallery/iTwin Demo.html | 11 +---- packages/engine/Source/Core/ITwinPlatform.js | 50 ++++---------------- packages/engine/Source/Scene/ITwinData.js | 47 ++++++++---------- 3 files changed, 33 insertions(+), 75 deletions(-) diff --git a/Apps/Sandcastle/gallery/iTwin Demo.html b/Apps/Sandcastle/gallery/iTwin Demo.html index dc87286bcd24..27047ba7d14d 100644 --- a/Apps/Sandcastle/gallery/iTwin Demo.html +++ b/Apps/Sandcastle/gallery/iTwin Demo.html @@ -38,7 +38,6 @@ // https://developer.bentley.com/my-itwins/b4a30036-0456-49ea-a439-3fcd9365e24e/home/ // const imodelId = "2852c3d7-00c3-4b5d-a0ce-82bbde4f061e"; const imodelId = "88673c1d-12b8-48f1-8beb-5000d0edbd0b"; - const changesetId = ""; // Grabbed mapping from the iTwin Viewer const classes = { @@ -123,18 +122,12 @@ statusOutput.innerText = "Creating Tileset"; - let tileset = await Cesium.ITwinData.createTilesetFromModelId( - imodelId, - changesetId, - ); + let tileset = await Cesium.ITwinData.createTilesetFromModelId(imodelId); if (!Cesium.defined(tileset)) { // TODO: this is temporary, we should not have to call the Start Export route ever after // auto generation is set up statusOutput.innerText = "Starting export"; - const exportId = await Cesium.ITwinPlatform.createExportForModelId( - imodelId, - changesetId, - ); + const exportId = await Cesium.ITwinPlatform.createExportForModelId(imodelId); statusOutput.innerText = "Creating Tileset from export"; tileset = await Cesium.ITwinData.createTilesetFromExportId(exportId); } diff --git a/packages/engine/Source/Core/ITwinPlatform.js b/packages/engine/Source/Core/ITwinPlatform.js index 0131e8efd5ee..c968ed48dd29 100644 --- a/packages/engine/Source/Core/ITwinPlatform.js +++ b/packages/engine/Source/Core/ITwinPlatform.js @@ -61,6 +61,7 @@ import RuntimeError from "./RuntimeError.js"; const ITwinPlatform = {}; /** + * Status states for a mesh-export export * @enum {string} */ ITwinPlatform.ExportStatus = Object.freeze({ @@ -71,6 +72,7 @@ ITwinPlatform.ExportStatus = Object.freeze({ }); /** + * Types of mesh-export exports. CesiumJS only supports loading 3DTILES type exports * @enum {string} */ ITwinPlatform.ExportType = Object.freeze({ @@ -80,15 +82,7 @@ ITwinPlatform.ExportType = Object.freeze({ }); /** - * Gets or sets the default iTwin access token. - * - * TODO: I'm not sure we can even do this kind of access token. Each route seems to need it's own scopes - * and we may not be able to guarantee this "top level token" has them all - * So far we use - * `mesh-export:read` for loading meshes GET /mesh-export(s) - * `mesh-export:modify` if we want to include a function to create an export - * `itwin-platform` if we want to use the iModel shares ourselves GET /imodels/{id}/shares - * Seems the `itwin-platform` scope should apply to everything but the docs are a little unclear + * Gets or sets the default iTwin access token. This token should have the itwin-platform scope. * * @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy. * @@ -119,6 +113,7 @@ ITwinPlatform.apiEndpoint = new Resource({ * @throws {RuntimeError} Requested export is not available * @throws {RuntimeError} Too many requests * @throws {RuntimeError} Unknown request failure + * TODO: remove? this is used when we're looping to wait for jobs to finish */ ITwinPlatform.getExport = async function (exportId) { //>>includeStart('debug', pragmas.debug); @@ -159,25 +154,22 @@ ITwinPlatform.getExport = async function (exportId) { }; /** - * Get the list of exports for the given iModel + changeset + * Get the list of exports for the specified iModel at it's most current version. This will only return exports with {@link ITwinPlatform.ExportType} of 3DTILES. * * @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy. * - * @param {string} iModelId - * @param {string} [changesetId] + * @param {string} iModelId iModel id * * @throws {RuntimeError} Unauthorized, bad token, wrong scopes or headers bad. * @throws {RuntimeError} Not allowed, forbidden * @throws {RuntimeError} Unprocessable Entity * @throws {RuntimeError} Too many requests * @throws {RuntimeError} Unknown request failure + * @returns {Promise<{exports: Export[]}>} */ -ITwinPlatform.getExports = async function (iModelId, changesetId) { +ITwinPlatform.getExports = async function (iModelId) { //>>includeStart('debug', pragmas.debug); Check.typeOf.string("iModelId", iModelId); - if (defined(changesetId)) { - Check.typeOf.string("changesetId", changesetId); - } if (!defined(ITwinPlatform.defaultAccessToken)) { throw new DeveloperError("Must set ITwin.defaultAccessToken first"); } @@ -190,12 +182,8 @@ ITwinPlatform.getExports = async function (iModelId, changesetId) { }; // obtain export for specified export id - // TODO: if we do include the clientVersion what should it be set to? can we sync it with the package.json? const url = new URL(`${ITwinPlatform.apiEndpoint}mesh-export`); url.searchParams.set("iModelId", iModelId); - if (defined(changesetId) && changesetId !== "") { - url.searchParams.set("changesetId", changesetId); - } url.searchParams.set("exportType", ITwinPlatform.ExportType["3DTILES"]); url.searchParams.set("$top", "1"); url.searchParams.set("client", "CesiumJS"); @@ -230,36 +218,19 @@ ITwinPlatform.getExports = async function (iModelId, changesetId) { }; /** - * Start the export process for the given iModel + changeset. - * * TODO: REMOVE THIS FUNCTION! Auto generation of exports for the 3DTILES type is planned very soon * and will be the desired way of interacting with iModels through exports. This function is here * just while we continue testing during the PR process. - * - * @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy. - * - * @param {string} iModelId - * @param {string} [changesetId] - * - * @throws {RuntimeError} Unauthorized, bad token, wrong scopes or headers bad. - * @throws {RuntimeError} Not allowed, forbidden - * @throws {RuntimeError} Unprocessable: Cannot create export job - * @throws {RuntimeError} Too many requests - * @throws {RuntimeError} Unknown request failure + * @deprecated */ -ITwinPlatform.createExportForModelId = async function (iModelId, changesetId) { +ITwinPlatform.createExportForModelId = async function (iModelId) { //>>includeStart('debug', pragmas.debug); Check.typeOf.string("iModelId", iModelId); - if (defined(changesetId)) { - Check.typeOf.string("changesetId", changesetId); - } if (!defined(ITwinPlatform.defaultAccessToken)) { throw new DeveloperError("Must set ITwin.defaultAccessToken first"); } //>>includeEnd('debug') - changesetId = changesetId ?? ""; - const requestOptions = { method: "POST", headers: { @@ -269,7 +240,6 @@ ITwinPlatform.createExportForModelId = async function (iModelId, changesetId) { }, body: JSON.stringify({ iModelId, - changesetId, exportType: ITwinPlatform.ExportType["3DTILES"], }), }; diff --git a/packages/engine/Source/Scene/ITwinData.js b/packages/engine/Source/Scene/ITwinData.js index e64307798e99..f822b4d9bc97 100644 --- a/packages/engine/Source/Scene/ITwinData.js +++ b/packages/engine/Source/Scene/ITwinData.js @@ -11,7 +11,8 @@ function delay(ms) { /** * @param {Export} exportObj - * @param {Cesium3DTileset.ConstructorOptions} [options] + * @param {Cesium3DTileset.ConstructorOptions} [options] Object containing options to pass to an internally created {@link Cesium3DTileset}. + * @returns {Promise} */ async function loadExport(exportObj, options) { //>>includeStart('debug', pragmas.debug); @@ -51,16 +52,24 @@ async function loadExport(exportObj, options) { return Cesium3DTileset.fromUrl(resource, options); } +/** + * Methods for loading iTwin platform data into CesiumJS + * + * @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy. + * + * @see ITwinPlatform to set the API token and access api functions + * @namespace ITwinData + */ const ITwinData = {}; /** - * Creates a {@link Cesium3DTileset} instance for the Google Photorealistic 3D Tiles tileset. + * Creates a {@link Cesium3DTileset} instance for the given export id. * * @function * @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy. * * @param {string} exportId - * @param {Cesium3DTileset.ConstructorOptions} [options] An object describing initialization options. + * @param {Cesium3DTileset.ConstructorOptions} [options] Object containing options to pass to an internally created {@link Cesium3DTileset}. * @returns {Promise} * * @throws {RuntimeError} Wrong export type @@ -68,47 +77,33 @@ const ITwinData = {}; * * @example * TODO: example after API finalized + * @deprecated */ ITwinData.createTilesetFromExportId = async function (exportId, options) { - options = options ?? {}; - const result = await ITwinPlatform.getExport(exportId); const tileset = await loadExport(result.export, options); return tileset; }; /** - * Check the exports for the given iModel + changeset combination for any that - * have the desired CESIUM type and returns the first one that matches as a new tileset. - * - * If there is not a CESIUM export you can create it using {@link ITwinPlatform.createExportForModelId} - * - * This function assumes one export per type per "iModel id + changeset id". If you need to create - * multiple exports per "iModel id + changeset id" you should switch to using {@link ITwinData} - * with the export id directly + * Loads the export for the specified iModel with the export type that CesiumJS can load and returns + * a tileset created from that export. * * @example * TODO: example after API finalized * * @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy. * - * @param {string} iModelId - * @param {string} changesetId - * @param {Cesium3DTileset.ConstructorOptions} options + * @param {string} iModelId The id of the iModel to load + * @param {Cesium3DTileset.ConstructorOptions} [options] Object containing options to pass to an internally created {@link Cesium3DTileset}. + * @returns {Promise} Will return undefined if there is no export for the given iModel id * * @throws {RuntimeError} Wrong export type * @throws {RuntimeError} Export did not complete in time. */ -ITwinData.createTilesetFromModelId = async function ( - iModelId, - changesetId, - options, -) { - const { exports } = await ITwinPlatform.getExports(iModelId, changesetId); - const cesiumExport = exports.find( - (exportObj) => - exportObj.request?.exportType === ITwinPlatform.ExportType["3DTILES"], - ); +ITwinData.createTilesetFromModelId = async function (iModelId, options) { + const { exports } = await ITwinPlatform.getExports(iModelId); + const cesiumExport = exports[0]; if (!defined(cesiumExport)) { return; } From b8c1ac54bb7914451ae43806bc544114ef190197 Mon Sep 17 00:00:00 2001 From: jjspace <8007967+jjspace@users.noreply.github.com> Date: Tue, 19 Nov 2024 16:40:03 -0500 Subject: [PATCH 095/175] remove get export and creation routes --- Apps/Sandcastle/gallery/iTwin Demo.html | 10 +- packages/engine/Source/Core/ITwinPlatform.js | 115 +------------------ packages/engine/Source/Scene/ITwinData.js | 53 ++------- 3 files changed, 11 insertions(+), 167 deletions(-) diff --git a/Apps/Sandcastle/gallery/iTwin Demo.html b/Apps/Sandcastle/gallery/iTwin Demo.html index 27047ba7d14d..de045f350659 100644 --- a/Apps/Sandcastle/gallery/iTwin Demo.html +++ b/Apps/Sandcastle/gallery/iTwin Demo.html @@ -122,15 +122,7 @@ statusOutput.innerText = "Creating Tileset"; - let tileset = await Cesium.ITwinData.createTilesetFromModelId(imodelId); - if (!Cesium.defined(tileset)) { - // TODO: this is temporary, we should not have to call the Start Export route ever after - // auto generation is set up - statusOutput.innerText = "Starting export"; - const exportId = await Cesium.ITwinPlatform.createExportForModelId(imodelId); - statusOutput.innerText = "Creating Tileset from export"; - tileset = await Cesium.ITwinData.createTilesetFromExportId(exportId); - } + const tileset = await Cesium.ITwinData.createTilesetFromModelId(imodelId); scene.primitives.add(tileset); tileset.colorBlendMode = Cesium.Cesium3DTileColorBlendMode.REPLACE; diff --git a/packages/engine/Source/Core/ITwinPlatform.js b/packages/engine/Source/Core/ITwinPlatform.js index c968ed48dd29..134c5a553f83 100644 --- a/packages/engine/Source/Core/ITwinPlatform.js +++ b/packages/engine/Source/Core/ITwinPlatform.js @@ -102,61 +102,11 @@ ITwinPlatform.apiEndpoint = new Resource({ url: "https://api.bentley.com", }); -/** - * Get the export object for the specified export id - * - * @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy. - * - * @param {string} exportId - * - * @throws {RuntimeError} Unauthorized, bad token, wrong scopes or headers bad. - * @throws {RuntimeError} Requested export is not available - * @throws {RuntimeError} Too many requests - * @throws {RuntimeError} Unknown request failure - * TODO: remove? this is used when we're looping to wait for jobs to finish - */ -ITwinPlatform.getExport = async function (exportId) { - //>>includeStart('debug', pragmas.debug); - Check.typeOf.string("exportId", exportId); - if (!defined(ITwinPlatform.defaultAccessToken)) { - throw new DeveloperError("Must set ITwin.defaultAccessToken first"); - } - //>>includeEnd('debug') - - const headers = { - Authorization: `Bearer ${ITwinPlatform.defaultAccessToken}`, - Accept: "application/vnd.bentley.itwin-platform.v1+json", - }; - - // obtain export for specified export id - const url = `${ITwinPlatform.apiEndpoint}mesh-export/${exportId}`; - - // TODO: this request is _really_ slow, like 7 whole second alone for me - // Arun said this was kinda normal but to keep track of the `x-correlation-id` of any that take EXTRA long - const response = await fetch(url, { headers }); - if (!response.ok) { - const result = await response.json(); - if (response.status === 401) { - throw new RuntimeError( - `Unauthorized, bad token, wrong scopes or headers bad. ${result.error.details[0].code}`, - ); - } else if (response.status === 404) { - throw new RuntimeError(`Requested export is not available ${exportId}`); - } else if (response.status === 429) { - throw new RuntimeError("Too many requests"); - } - throw new RuntimeError(`Unknown request failure ${response.status}`); - } - - /** @type {ExportResponse} */ - const result = await response.json(); - return result; -}; - /** * Get the list of exports for the specified iModel at it's most current version. This will only return exports with {@link ITwinPlatform.ExportType} of 3DTILES. * * @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy. + * @private * * @param {string} iModelId iModel id * @@ -217,67 +167,4 @@ ITwinPlatform.getExports = async function (iModelId) { return result; }; -/** - * TODO: REMOVE THIS FUNCTION! Auto generation of exports for the 3DTILES type is planned very soon - * and will be the desired way of interacting with iModels through exports. This function is here - * just while we continue testing during the PR process. - * @deprecated - */ -ITwinPlatform.createExportForModelId = async function (iModelId) { - //>>includeStart('debug', pragmas.debug); - Check.typeOf.string("iModelId", iModelId); - if (!defined(ITwinPlatform.defaultAccessToken)) { - throw new DeveloperError("Must set ITwin.defaultAccessToken first"); - } - //>>includeEnd('debug') - - const requestOptions = { - method: "POST", - headers: { - Authorization: `Bearer ${ITwinPlatform.defaultAccessToken}`, - Accept: "application/vnd.bentley.itwin-platform.v1+json", - "Content-Type": "application/json", - }, - body: JSON.stringify({ - iModelId, - exportType: ITwinPlatform.ExportType["3DTILES"], - }), - }; - - // initiate mesh export - const response = await fetch( - `${ITwinPlatform.apiEndpoint}mesh-export/`, - requestOptions, - ); - - if (!response.ok) { - const result = await response.json(); - if (response.status === 401) { - console.error( - result.error.code, - result.error.message, - result.error.details, - ); - throw new RuntimeError( - "Unauthorized, bad token, wrong scopes or headers bad", - ); - } else if (response.status === 403) { - console.error(result.error.code, result.error.message); - throw new RuntimeError("Not allowed, forbidden"); - } else if (response.status === 422) { - console.error(result.error.code, result.error.message); - console.error(result.error.details); - throw new RuntimeError("Unprocessable: Cannot create export job"); - } else if (response.status === 429) { - throw new RuntimeError("Too many requests"); - } - - throw new RuntimeError(`Unknown request failure ${response.status}`); - } - - /** @type {ExportResponse} */ - const result = await response.json(); - return result.export.id; -}; - export default ITwinPlatform; diff --git a/packages/engine/Source/Scene/ITwinData.js b/packages/engine/Source/Scene/ITwinData.js index f822b4d9bc97..ef5769431748 100644 --- a/packages/engine/Source/Scene/ITwinData.js +++ b/packages/engine/Source/Scene/ITwinData.js @@ -5,10 +5,6 @@ import ITwinPlatform from "../Core/ITwinPlatform.js"; import RuntimeError from "../Core/RuntimeError.js"; import Check from "../Core/Check.js"; -function delay(ms) { - return new Promise((res) => setTimeout(res, ms)); -} - /** * @param {Export} exportObj * @param {Cesium3DTileset.ConstructorOptions} [options] Object containing options to pass to an internally created {@link Cesium3DTileset}. @@ -19,24 +15,13 @@ async function loadExport(exportObj, options) { Check.defined("exportObj", exportObj); //>>includeEnd('debug') - let status = exportObj.status; - if (exportObj.request.exportType !== ITwinPlatform.ExportType["3DTILES"]) { throw new RuntimeError(`Wrong export type ${exportObj.request.exportType}`); } - - const timeoutAfter = 300000; - const start = Date.now(); - // wait until the export is complete - while (status !== ITwinPlatform.ExportStatus.Complete) { - await delay(5000); - exportObj = (await ITwinPlatform.getExport(exportObj.id)).export; - status = exportObj.status; - console.log(`Export is ${status}`); - - if (Date.now() - start > timeoutAfter) { - throw new RuntimeError("Export did not complete in time."); - } + if (exportObj.status !== ITwinPlatform.ExportStatus.Complete) { + throw new RuntimeError( + `Export is not completed. ${exportObj.id} - ${exportObj.status}`, + ); } // Convert the link to the tileset url while preserving the search paramaters @@ -62,33 +47,13 @@ async function loadExport(exportObj, options) { */ const ITwinData = {}; -/** - * Creates a {@link Cesium3DTileset} instance for the given export id. - * - * @function - * @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy. - * - * @param {string} exportId - * @param {Cesium3DTileset.ConstructorOptions} [options] Object containing options to pass to an internally created {@link Cesium3DTileset}. - * @returns {Promise} - * - * @throws {RuntimeError} Wrong export type - * @throws {RuntimeError} Export did not complete in time. - * - * @example - * TODO: example after API finalized - * @deprecated - */ -ITwinData.createTilesetFromExportId = async function (exportId, options) { - const result = await ITwinPlatform.getExport(exportId); - const tileset = await loadExport(result.export, options); - return tileset; -}; - /** * Loads the export for the specified iModel with the export type that CesiumJS can load and returns * a tileset created from that export. * + * If the export is not finished processing this will throw an error. It is up to the caller + * to re-attempt loading at a later time + * * @example * TODO: example after API finalized * @@ -98,8 +63,8 @@ ITwinData.createTilesetFromExportId = async function (exportId, options) { * @param {Cesium3DTileset.ConstructorOptions} [options] Object containing options to pass to an internally created {@link Cesium3DTileset}. * @returns {Promise} Will return undefined if there is no export for the given iModel id * - * @throws {RuntimeError} Wrong export type - * @throws {RuntimeError} Export did not complete in time. + * @throws {RuntimeError} Wrong export type [type] + * @throws {RuntimeError} Export is not completed. [id] - [status] */ ITwinData.createTilesetFromModelId = async function (iModelId, options) { const { exports } = await ITwinPlatform.getExports(iModelId); From 958756eea714c26998405fb6956532e2b4a86170 Mon Sep 17 00:00:00 2001 From: jjspace <8007967+jjspace@users.noreply.github.com> Date: Tue, 19 Nov 2024 16:53:29 -0500 Subject: [PATCH 096/175] update sandcastle for exports still processing --- Apps/Sandcastle/gallery/iTwin Demo.html | 27 ++++++++++++++++++-- packages/engine/Source/Core/ITwinPlatform.js | 2 ++ packages/engine/Source/Scene/ITwinData.js | 3 ++- 3 files changed, 29 insertions(+), 3 deletions(-) diff --git a/Apps/Sandcastle/gallery/iTwin Demo.html b/Apps/Sandcastle/gallery/iTwin Demo.html index de045f350659..7bee11033cad 100644 --- a/Apps/Sandcastle/gallery/iTwin Demo.html +++ b/Apps/Sandcastle/gallery/iTwin Demo.html @@ -119,10 +119,33 @@ statusOutput.innerText = "Starting export"; const start = Date.now(); + const delay = (ms) => new Promise((res) => setTimeout(res, ms)); statusOutput.innerText = "Creating Tileset"; - - const tileset = await Cesium.ITwinData.createTilesetFromModelId(imodelId); + let tileset; + try { + tileset = await Cesium.ITwinData.createTilesetFromModelId(imodelId); + } catch (error) { + if (!error.message.includes("not completed")) { + throw error; + } + statusOutput.innerText = "Tileset not completed, retrying..."; + + const timeoutAfter = 300000; + // wait until the export is complete + while (Date.now() - start > timeoutAfter) { + await delay(5000); + try { + tileset = await Cesium.ITwinData.createTilesetFromModelId(imodelId); + break; + } catch (error) { + if (!error.message.includes("not completed")) { + throw error; + } + } + } + throw new Cesium.RuntimeError("Export did not complete in time."); + } scene.primitives.add(tileset); tileset.colorBlendMode = Cesium.Cesium3DTileColorBlendMode.REPLACE; diff --git a/packages/engine/Source/Core/ITwinPlatform.js b/packages/engine/Source/Core/ITwinPlatform.js index 134c5a553f83..defe07107bbb 100644 --- a/packages/engine/Source/Core/ITwinPlatform.js +++ b/packages/engine/Source/Core/ITwinPlatform.js @@ -135,6 +135,8 @@ ITwinPlatform.getExports = async function (iModelId) { const url = new URL(`${ITwinPlatform.apiEndpoint}mesh-export`); url.searchParams.set("iModelId", iModelId); url.searchParams.set("exportType", ITwinPlatform.ExportType["3DTILES"]); + // TODO: If we're only requesting the top 1 is there a chance it's `Invalid` instead of `Complete` + // and never possible to load it? url.searchParams.set("$top", "1"); url.searchParams.set("client", "CesiumJS"); /* global CESIUM_VERSION */ diff --git a/packages/engine/Source/Scene/ITwinData.js b/packages/engine/Source/Scene/ITwinData.js index ef5769431748..bfe560ec8440 100644 --- a/packages/engine/Source/Scene/ITwinData.js +++ b/packages/engine/Source/Scene/ITwinData.js @@ -55,7 +55,8 @@ const ITwinData = {}; * to re-attempt loading at a later time * * @example - * TODO: example after API finalized + * const tileset = await Cesium.ITwinData.createTilesetFromModelId(imodelId); + * viewer.scene.primitives.add(tileset); * * @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy. * From 3169d800b30ed3b236d9769f1846ca8d3ef019e9 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Fri, 1 Nov 2024 19:32:36 +0200 Subject: [PATCH 097/175] CONTRIBUTORS.md --- CONTRIBUTORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 5bd6a8a5cdaf..b2dc5773acd4 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -414,4 +414,5 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for details on how to contribute to Cesiu - [Adam Wirth](https://github.com/adamwirth) - [Javier Sanchez](https://github.com/jvrjsanchez) - [Jérôme Fayot](https://github.com/jfayot) +- [Michael Nusair](https://github.com/mnpcmw6444) - [Kirn Kim](https://github.com/squrki) From 4b50d48b1103465f2b1321c4987498e6a04101b5 Mon Sep 17 00:00:00 2001 From: "mnpcmw6444@gmail.com" Date: Wed, 20 Nov 2024 16:39:17 +0200 Subject: [PATCH 098/175] better fix --- packages/engine/Source/Scene/Label.js | 16 ++++ .../engine/Specs/Scene/LabelCollectionSpec.js | 78 +++++++++++++++++++ 2 files changed, 94 insertions(+) diff --git a/packages/engine/Source/Scene/Label.js b/packages/engine/Source/Scene/Label.js index 7196877768a9..18059255dfb0 100644 --- a/packages/engine/Source/Scene/Label.js +++ b/packages/engine/Source/Scene/Label.js @@ -391,6 +391,7 @@ Object.defineProperties(Label.prototype, { return this._text; }, set: function (value) { + value = Label.filterUnsupportedCharacters(value); //>>includeStart('debug', pragmas.debug); if (!defined(value)) { throw new DeveloperError("value is required."); @@ -1346,6 +1347,21 @@ Label.getScreenSpaceBoundingBox = function ( return result; }; +/** + * Removes control characters, which will cause an error when rendering a glyph. + * @private + * @param {string} text The original label text + * @returns {string} The renderable filtered text + */ +Label.filterUnsupportedCharacters = function (text) { + const problematicCharactersRegex = new RegExp( + // eslint-disable-next-line no-control-regex + /[\u0000-\u001F\u202a-\u206f\u200b-\u200f]/, + "g", + ); + return text.replace(problematicCharactersRegex, ""); +}; + /** * Determines if this label equals another label. Labels are equal if all their properties * are equal. Labels in different collections can be equal. diff --git a/packages/engine/Specs/Scene/LabelCollectionSpec.js b/packages/engine/Specs/Scene/LabelCollectionSpec.js index 3f163690e81e..e4488420cba2 100644 --- a/packages/engine/Specs/Scene/LabelCollectionSpec.js +++ b/packages/engine/Specs/Scene/LabelCollectionSpec.js @@ -2560,6 +2560,84 @@ describe( }); }); + it("filterUnsupportedCharacters removes unicode characters from text only if they cause render issues", function () { + const text = "a😀b"; + const expectedText = "a😀b"; + expect(Label.filterUnsupportedCharacters(text)).toEqual(expectedText); + }); + + it("filterUnsupportedCharacters removes unicode characters from text only if they cause render issues", function () { + const text = "awe2!$34f❤️b"; + const expectedText = "awe2!$34f❤️b"; + expect(Label.filterUnsupportedCharacters(text)).toEqual(expectedText); + }); + + it("filterUnsupportedCharacters removes unicode characters from text only if they cause render issues", function () { + const text = "lakneklf\u200fsldknfklf"; + const expectedText = "lakneklfsldknfklf"; + expect(Label.filterUnsupportedCharacters(text)).toEqual(expectedText); + }); + + it("filterUnsupportedCharacters removes unicode characters from text only if they cause render issues", function () { + const text = "test \u200f - with Right-to-Left Mark (RLM)"; + const expectedText = "test - with Right-to-Left Mark (RLM)"; + expect(Label.filterUnsupportedCharacters(text)).toEqual(expectedText); + }); + + it("filterUnsupportedCharacters removes unicode characters from text only if they cause render issues", function () { + const text = "test \u200b - with Zero-Width Space"; + const expectedText = "test - with Zero-Width Space"; + expect(Label.filterUnsupportedCharacters(text)).toEqual(expectedText); + }); + + it("filterUnsupportedCharacters removes unicode characters from text only if they cause render issues", function () { + const text = "test \u202a - with Left-to-Right Embedding"; + const expectedText = "test - with Left-to-Right Embedding"; + expect(Label.filterUnsupportedCharacters(text)).toEqual(expectedText); + }); + + it("filterUnsupportedCharacters removes unicode characters from text only if they cause render issues", function () { + const text = "test 😀 - Emoji"; + const expectedText = "test 😀 - Emoji"; + expect(Label.filterUnsupportedCharacters(text)).toEqual(expectedText); + }); + + it("filterUnsupportedCharacters removes unicode characters from text only if they cause render issues", function () { + const text = "test ❤️ - Emoji"; + const expectedText = "test ❤️ - Emoji"; + expect(Label.filterUnsupportedCharacters(text)).toEqual(expectedText); + }); + + it("filterUnsupportedCharacters removes unicode characters from text only if they cause render issues", function () { + const text = "test 🌍 - Emoji"; + const expectedText = "test 🌍 - Emoji"; + expect(Label.filterUnsupportedCharacters(text)).toEqual(expectedText); + }); + + it("filterUnsupportedCharacters removes unicode characters from text only if they cause render issues", function () { + const text = "test \u2060 - with Word Joiner"; + const expectedText = "test - with Word Joiner"; + expect(Label.filterUnsupportedCharacters(text)).toEqual(expectedText); + }); + + it("filterUnsupportedCharacters removes unicode characters from text only if they cause render issues", function () { + const text = "test \u0000 - with Null Character"; + const expectedText = "test - with Null Character"; + expect(Label.filterUnsupportedCharacters(text)).toEqual(expectedText); + }); + + it("filterUnsupportedCharacters removes unicode characters from text only if they cause render issues", function () { + const text = "test \u000A - with Line Feed (LF)"; + const expectedText = "test - with Line Feed (LF)"; + expect(Label.filterUnsupportedCharacters(text)).toEqual(expectedText); + }); + + it("filterUnsupportedCharacters removes unicode characters from text only if they cause render issues", function () { + const text = "test \u001F - with Unit Separator (US)"; + const expectedText = "test - with Unit Separator (US)"; + expect(Label.filterUnsupportedCharacters(text)).toEqual(expectedText); + }); + describe("height referenced labels", function () { beforeEach(function () { scene.globe = new Globe(); From 5014ecb0052f1123f87bf92bfda14f386c3ff3fe Mon Sep 17 00:00:00 2001 From: Mark Dane Date: Wed, 20 Nov 2024 10:56:23 -0500 Subject: [PATCH 099/175] Add links to terms of service --- packages/engine/Source/Core/BingMapsGeocoderService.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/engine/Source/Core/BingMapsGeocoderService.js b/packages/engine/Source/Core/BingMapsGeocoderService.js index 647658fcd2d3..bd299d874026 100644 --- a/packages/engine/Source/Core/BingMapsGeocoderService.js +++ b/packages/engine/Source/Core/BingMapsGeocoderService.js @@ -10,6 +10,8 @@ const url = "https://dev.virtualearth.net/REST/v1/Locations"; /** * Provides geocoding through Bing Maps. + * + * @see {@link https://www.microsoft.com/en-us/maps/bing-maps/product|Microsoft Bing Maps Platform APIs Terms Of Use} * @alias BingMapsGeocoderService * @constructor * From c05d122b36581becdd047db6c8282558435bd99b Mon Sep 17 00:00:00 2001 From: Mark Dane Date: Wed, 20 Nov 2024 10:56:44 -0500 Subject: [PATCH 100/175] More renames of geocodeProvider -> geocodeProviderType --- .../engine/Source/Core/IonGeocoderService.js | 30 +++++++++++-------- .../Specs/Core/IonGeocoderServiceSpec.js | 30 +++++++++---------- packages/widgets/Source/Viewer/Viewer.js | 5 +++- packages/widgets/Specs/Viewer/ViewerSpec.js | 2 +- 4 files changed, 37 insertions(+), 30 deletions(-) diff --git a/packages/engine/Source/Core/IonGeocoderService.js b/packages/engine/Source/Core/IonGeocoderService.js index db0cfe3dfc81..c91785229f04 100644 --- a/packages/engine/Source/Core/IonGeocoderService.js +++ b/packages/engine/Source/Core/IonGeocoderService.js @@ -9,17 +9,19 @@ import PeliasGeocoderService from "./PeliasGeocoderService.js"; import Resource from "./Resource.js"; /** - * @param {*} geocodeProvider + * @param {*} geocodeProviderType * @throws {DeveloperError} * @private */ -function validateIonGeocodeProviderType(geocodeProvider) { +function validateIonGeocodeProviderType(geocodeProviderType) { if ( !Object.values(IonGeocodeProviderType).some( - (value) => value === geocodeProvider, + (value) => value === geocodeProviderType, ) ) { - throw new DeveloperError(`Invalid geocodeProvider: "${geocodeProvider}"`); + throw new DeveloperError( + `Invalid geocodeProviderType: "${geocodeProviderType}"`, + ); } } @@ -48,7 +50,7 @@ function queryParameterToProvider(parameter) { * @param {Scene} options.scene The scene * @param {string} [options.accessToken=Ion.defaultAccessToken] The access token to use. * @param {string|Resource} [options.server=Ion.defaultServer] The resource to the Cesium ion API server. - * @param {IonGeocodeProviderType} [options.geocodeProvider=IonGeocodeProviderType.DEFAULT] The geocoder the Cesium ion API server should use to fulfill this request. + * @param {IonGeocodeProviderType} [options.geocodeProviderType=IonGeocodeProviderType.DEFAULT] The geocoder the Cesium ion API server should use to fulfill this request. * * @see Ion */ @@ -59,11 +61,11 @@ function IonGeocoderService(options) { Check.typeOf.object("options.scene", options.scene); //>>includeEnd('debug'); - const geocodeProvider = defaultValue( - options.geocodeProvider, + const geocodeProviderType = defaultValue( + options.geocodeProviderType, IonGeocodeProviderType.DEFAULT, ); - validateIonGeocodeProviderType(geocodeProvider); + validateIonGeocodeProviderType(geocodeProviderType); const accessToken = defaultValue(options.accessToken, Ion.defaultAccessToken); const server = Resource.createIfNeeded( @@ -89,7 +91,9 @@ function IonGeocoderService(options) { this._accessToken = accessToken; this._server = server; this._pelias = new PeliasGeocoderService(searchEndpoint); - this.geocodeProvider = geocodeProvider; + // geocoderProviderType isn't stored here directly but instead relies on the + // query parameters of this._pelias.url. Use the setter logic to update value. + this.geocodeProviderType = geocodeProviderType; } Object.defineProperties(IonGeocoderService.prototype, { @@ -111,17 +115,17 @@ Object.defineProperties(IonGeocoderService.prototype, { * @type {IonGeocodeProviderType} * @default IonGeocodeProviderType.DEFAULT */ - geocodeProvider: { + geocodeProviderType: { get: function () { return queryParameterToProvider( this._pelias.url.queryParameters["geocoder"], ); }, - set: function (geocodeProvider) { - validateIonGeocodeProviderType(geocodeProvider); + set: function (geocodeProviderType) { + validateIonGeocodeProviderType(geocodeProviderType); const query = { ...this._pelias.url.queryParameters, - geocoder: providerToQueryParameter(geocodeProvider), + geocoder: providerToQueryParameter(geocodeProviderType), }; // Delete the geocoder parameter to prevent sending &geocoder=undefined in the query if (!defined(query.geocoder)) { diff --git a/packages/engine/Specs/Core/IonGeocoderServiceSpec.js b/packages/engine/Specs/Core/IonGeocoderServiceSpec.js index d2254501bf59..719a762daffc 100644 --- a/packages/engine/Specs/Core/IonGeocoderServiceSpec.js +++ b/packages/engine/Specs/Core/IonGeocoderServiceSpec.js @@ -28,24 +28,24 @@ describe("Core/IonGeocoderService", function () { expect(service._accessToken).toEqual(Ion.defaultAccessToken); expect(service._server.url).toEqual(Ion.defaultServer.url); - expect(service.geocodeProvider).toEqual(IonGeocodeProviderType.DEFAULT); + expect(service.geocodeProviderType).toEqual(IonGeocodeProviderType.DEFAULT); }); it("creates with specified parameters", function () { const accessToken = "123456"; const server = "http://not.ion.invalid/"; - const geocodeProvider = IonGeocodeProviderType.GOOGLE; + const geocodeProviderType = IonGeocodeProviderType.GOOGLE; const service = new IonGeocoderService({ accessToken: accessToken, server: server, scene: scene, - geocodeProvider, + geocodeProviderType, }); expect(service._accessToken).toEqual(accessToken); expect(service._server.url).toEqual(server); - expect(service.geocodeProvider).toEqual(geocodeProvider); + expect(service.geocodeProviderType).toEqual(geocodeProviderType); }); it("calls inner geocoder and returns result", async function () { @@ -71,46 +71,46 @@ describe("Core/IonGeocoderService", function () { expect(service.credit).toBeUndefined(); }); - it("setting geocodeProvider updates _pelias.url for GOOGLE", function () { + it("setting geocodeProviderType updates _pelias.url for GOOGLE", function () { const service = new IonGeocoderService({ scene, geocoder: IonGeocodeProviderType.DEFAULT, }); - service.geocodeProvider = IonGeocodeProviderType.GOOGLE; + service.geocodeProviderType = IonGeocodeProviderType.GOOGLE; expect(service._pelias.url.queryParameters["geocoder"]).toEqual("google"); }); - it("setting geocodeProvider updates _pelias.url for BING", function () { + it("setting geocodeProviderType updates _pelias.url for BING", function () { const service = new IonGeocoderService({ scene, geocoder: IonGeocodeProviderType.DEFAULT, }); - service.geocodeProvider = IonGeocodeProviderType.BING; + service.geocodeProviderType = IonGeocodeProviderType.BING; expect(service._pelias.url.queryParameters["geocoder"]).toEqual("bing"); }); - it("setting geocodeProvider updates _pelias.url for DEFAULT", function () { + it("setting geocodeProviderType updates _pelias.url for DEFAULT", function () { const service = new IonGeocoderService({ scene, geocoder: IonGeocodeProviderType.GOOGLE, }); - service.geocodeProvider = IonGeocodeProviderType.DEFAULT; + service.geocodeProviderType = IonGeocodeProviderType.DEFAULT; const queryParameters = service._pelias.url.queryParameters; expect(queryParameters.geocoder).toBeUndefined(); // Make sure that it isn't 'geocoder: undefined' expect(queryParameters.hasOwnProperty("geocoder")).toBeFalse(); }); - it("throws if setting invalid geocodeProvider", function () { + it("throws if setting invalid geocodeProviderType", function () { expect( - () => new IonGeocoderService({ scene, geocodeProvider: "junk" }), - ).toThrowError(DeveloperError, /Invalid geocodeProvider/); + () => new IonGeocoderService({ scene, geocodeProviderType: "junk" }), + ).toThrowError(DeveloperError, /Invalid geocodeProviderType/); expect(() => { const service = new IonGeocoderService({ scene }); - service.geocodeProvider = "junk"; - }).toThrowError(DeveloperError, /Invalid geocodeProvider/); + service.geocodeProviderType = "junk"; + }).toThrowError(DeveloperError, /Invalid geocodeProviderType/); }); }); diff --git a/packages/widgets/Source/Viewer/Viewer.js b/packages/widgets/Source/Viewer/Viewer.js index ae39c71dfe8a..b00646389215 100644 --- a/packages/widgets/Source/Viewer/Viewer.js +++ b/packages/widgets/Source/Viewer/Viewer.js @@ -570,7 +570,10 @@ Either specify options.terrainProvider instead or set options.baseLayerPicker to let geocoderService; if (typeof options.geocoder === "string") { geocoderService = [ - new IonGeocoderService({ scene, geocodeProvider: options.geocoder }), + new IonGeocoderService({ + scene, + geocodeProviderType: options.geocoder, + }), ]; } else if ( defined(options.geocoder) && diff --git a/packages/widgets/Specs/Viewer/ViewerSpec.js b/packages/widgets/Specs/Viewer/ViewerSpec.js index f40e3bba1b32..822df41c034b 100644 --- a/packages/widgets/Specs/Viewer/ViewerSpec.js +++ b/packages/widgets/Specs/Viewer/ViewerSpec.js @@ -364,7 +364,7 @@ describe( expect(viewer.geocoder.viewModel._geocoderServices.length).toBe(1); const geocoderService = viewer.geocoder.viewModel._geocoderServices[0]; expect(geocoderService).toBeInstanceOf(IonGeocoderService); - expect(geocoderService.geocodeProvider).toEqual( + expect(geocoderService.geocodeProviderType).toEqual( IonGeocodeProviderType.GOOGLE, ); }); From 7049dbefa28b93f1898192d29eaefc9c0806ee84 Mon Sep 17 00:00:00 2001 From: jjspace <8007967+jjspace@users.noreply.github.com> Date: Wed, 20 Nov 2024 13:28:31 -0500 Subject: [PATCH 101/175] re-organize, resource instead of fetch, clean up types --- Apps/Sandcastle/gallery/iTwin Demo.html | 2 +- packages/engine/Source/Core/ITwinPlatform.js | 133 ++++++++----------- packages/engine/Source/Scene/ITwinData.js | 22 +-- 3 files changed, 71 insertions(+), 86 deletions(-) diff --git a/Apps/Sandcastle/gallery/iTwin Demo.html b/Apps/Sandcastle/gallery/iTwin Demo.html index 7bee11033cad..17e54c4f2294 100644 --- a/Apps/Sandcastle/gallery/iTwin Demo.html +++ b/Apps/Sandcastle/gallery/iTwin Demo.html @@ -134,7 +134,7 @@ const timeoutAfter = 300000; // wait until the export is complete while (Date.now() - start > timeoutAfter) { - await delay(5000); + await delay(15000); try { tileset = await Cesium.ITwinData.createTilesetFromModelId(imodelId); break; diff --git a/packages/engine/Source/Core/ITwinPlatform.js b/packages/engine/Source/Core/ITwinPlatform.js index defe07107bbb..734d2b150749 100644 --- a/packages/engine/Source/Core/ITwinPlatform.js +++ b/packages/engine/Source/Core/ITwinPlatform.js @@ -4,52 +4,6 @@ import DeveloperError from "./DeveloperError.js"; import Resource from "./Resource.js"; import RuntimeError from "./RuntimeError.js"; -/** - * @typedef {Object} GeometryOptions - * @property {boolean} includeLines - * @property {number} chordTol - * @property {number} angleTol - * @property {number} decimationTol - * @property {number} maxEdgeLength - * @property {number} minBRepFeatureSize - * @property {number} minLineStyleComponentSize - */ - -/** - * @typedef {Object} ViewDefinitionFilter - * @property {string[]} models Array of included model IDs. - * @property {string[]} categories Array of included category IDs. - * @property {string[]} neverDrawn Array of element IDs to filter out. - */ - -/** - * @typedef {Object} StartExport - * @property {string} iModelId - * @property {string} changesetId - * @property {ITwinPlatform.ExportType} exportType Type of mesh to create. Currently, only GLTF and 3DFT are supported and undocumented CESIUM option - * @property {GeometryOptions} geometryOptions - * @property {ViewDefinitionFilter} viewDefinitionFilter - */ - -/** - * @typedef {Object} Link - * @property {string} href - */ - -/** - * @typedef {Object} Export - * @property {string} id - * @property {string} displayName - * @property {ITwinPlatform.ExportStatus} status - * @property {StartExport} request - * @property {{mesh: Link}} _links - */ - -/** - * @typedef {Object} ExportResponse - * @property {Export} export - */ - /** * Default settings for accessing the iTwin platform. * @@ -63,6 +17,7 @@ const ITwinPlatform = {}; /** * Status states for a mesh-export export * @enum {string} + * @private */ ITwinPlatform.ExportStatus = Object.freeze({ NotStarted: "NotStarted", @@ -74,6 +29,7 @@ ITwinPlatform.ExportStatus = Object.freeze({ /** * Types of mesh-export exports. CesiumJS only supports loading 3DTILES type exports * @enum {string} + * @private */ ITwinPlatform.ExportType = Object.freeze({ IMODEL: "IMODEL", @@ -102,6 +58,35 @@ ITwinPlatform.apiEndpoint = new Resource({ url: "https://api.bentley.com", }); +/** + * @typedef {Object} ExportRequest + * @property {string} iModelId + * @property {string} changesetId + * @property {ITwinPlatform.ExportType} exportType Type of the export. CesiumJS only supports the 3DTILES type + */ + +/** + * @typedef {Object} Link + * @property {string} href + */ + +/** + * @typedef {Object} ExportRepresentation + * The export objects from get-exports when using return=representation + * @property {string} id Export id + * @property {string} displayName Name of the iModel + * @property {ITwinPlatform.ExportStatus} status Status of this export + * @property {string} lastModified + * @property {ExportRequest} request Object containing info about the export itself + * @property {{mesh: Link}} _links Object containing relevant links. For Exports this includes the access url for the mesh itself + */ + +/** + * @typedef {Object} GetExportsResponse + * @property {ExportRepresentation[]} exports The list of exports for the current page + * @property {{self: Link, next: Link | undefined, prev: Link | undefined}} _links Pagination links + */ + /** * Get the list of exports for the specified iModel at it's most current version. This will only return exports with {@link ITwinPlatform.ExportType} of 3DTILES. * @@ -115,7 +100,7 @@ ITwinPlatform.apiEndpoint = new Resource({ * @throws {RuntimeError} Unprocessable Entity * @throws {RuntimeError} Too many requests * @throws {RuntimeError} Unknown request failure - * @returns {Promise<{exports: Export[]}>} + * @returns {Promise} */ ITwinPlatform.getExports = async function (iModelId) { //>>includeStart('debug', pragmas.debug); @@ -125,48 +110,48 @@ ITwinPlatform.getExports = async function (iModelId) { } //>>includeEnd('debug') - const headers = { - Authorization: `Bearer ${ITwinPlatform.defaultAccessToken}`, - Accept: "application/vnd.bentley.itwin-platform.v1+json", - Prefer: "return=representation", // or return=minimal (the default) - }; - - // obtain export for specified export id - const url = new URL(`${ITwinPlatform.apiEndpoint}mesh-export`); - url.searchParams.set("iModelId", iModelId); - url.searchParams.set("exportType", ITwinPlatform.ExportType["3DTILES"]); - // TODO: If we're only requesting the top 1 is there a chance it's `Invalid` instead of `Complete` - // and never possible to load it? - url.searchParams.set("$top", "1"); - url.searchParams.set("client", "CesiumJS"); + const resource = new Resource({ + url: `${ITwinPlatform.apiEndpoint}mesh-export`, + headers: { + Authorization: `Bearer ${ITwinPlatform.defaultAccessToken}`, + Accept: "application/vnd.bentley.itwin-platform.v1+json", + Prefer: "return=representation", + }, + queryParameters: { + iModelId: iModelId, + exportType: ITwinPlatform.ExportType["3DTILES"], + // TODO: If we're only requesting the top 1 is there a chance it's `Invalid` instead of `Complete` + // and never possible to load it? + $top: "1", + client: "CesiumJS", + }, + }); /* global CESIUM_VERSION */ if (typeof CESIUM_VERSION !== "undefined") { - url.searchParams.set("clientVersion", CESIUM_VERSION); + resource.appendQueryParameters({ clientVersion: CESIUM_VERSION }); } - const response = await fetch(url, { headers }); - if (!response.ok) { - const result = await response.json(); - if (response.status === 401) { + try { + const response = await resource.fetchJson(); + return response; + } catch (error) { + const result = JSON.parse(error.response); + if (error.statusCode === 401) { throw new RuntimeError( `Unauthorized, bad token, wrong scopes or headers bad. ${result.error.details[0].code}`, ); - } else if (response.status === 403) { + } else if (error.statusCode === 403) { console.error(result.error.code, result.error.message); throw new RuntimeError("Not allowed, forbidden"); - } else if (response.status === 422) { + } else if (error.statusCode === 422) { throw new RuntimeError( `Unprocessable Entity:${result.error.code} ${result.error.message}`, ); - } else if (response.status === 429) { + } else if (error.statusCode === 429) { throw new RuntimeError("Too many requests"); } - throw new RuntimeError(`Unknown request failure ${response.status}`); + throw new RuntimeError(`Unknown request failure ${error.statusCode}`); } - - /** @type {{exports: Export[]}} */ - const result = await response.json(); - return result; }; export default ITwinPlatform; diff --git a/packages/engine/Source/Scene/ITwinData.js b/packages/engine/Source/Scene/ITwinData.js index bfe560ec8440..0659620bdb13 100644 --- a/packages/engine/Source/Scene/ITwinData.js +++ b/packages/engine/Source/Scene/ITwinData.js @@ -6,7 +6,17 @@ import RuntimeError from "../Core/RuntimeError.js"; import Check from "../Core/Check.js"; /** - * @param {Export} exportObj + * Methods for loading iTwin platform data into CesiumJS + * + * @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy. + * + * @see ITwinPlatform to set the API token and base api url + * @namespace ITwinData + */ +const ITwinData = {}; + +/** + * @param {ExportRepresentation} exportObj * @param {Cesium3DTileset.ConstructorOptions} [options] Object containing options to pass to an internally created {@link Cesium3DTileset}. * @returns {Promise} */ @@ -37,16 +47,6 @@ async function loadExport(exportObj, options) { return Cesium3DTileset.fromUrl(resource, options); } -/** - * Methods for loading iTwin platform data into CesiumJS - * - * @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy. - * - * @see ITwinPlatform to set the API token and access api functions - * @namespace ITwinData - */ -const ITwinData = {}; - /** * Loads the export for the specified iModel with the export type that CesiumJS can load and returns * a tileset created from that export. From e7b7ea2929c37c5f8628daf27ccfcbd858d30403 Mon Sep 17 00:00:00 2001 From: ggetz Date: Wed, 20 Nov 2024 13:32:49 -0500 Subject: [PATCH 102/175] Adjust setter, ensure line breaks and tabs still work --- packages/engine/Source/Scene/Label.js | 10 ++++------ packages/engine/Specs/Scene/LabelCollectionSpec.js | 4 ++-- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/packages/engine/Source/Scene/Label.js b/packages/engine/Source/Scene/Label.js index 18059255dfb0..61168a5ff2c0 100644 --- a/packages/engine/Source/Scene/Label.js +++ b/packages/engine/Source/Scene/Label.js @@ -391,7 +391,6 @@ Object.defineProperties(Label.prototype, { return this._text; }, set: function (value) { - value = Label.filterUnsupportedCharacters(value); //>>includeStart('debug', pragmas.debug); if (!defined(value)) { throw new DeveloperError("value is required."); @@ -401,8 +400,7 @@ Object.defineProperties(Label.prototype, { if (this._text !== value) { this._text = value; - // Strip soft-hyphen (auto-wrap) characters from input string - const renderedValue = value.replace(/\u00ad/g, ""); + const renderedValue = Label.filterUnsupportedCharacters(value); this._renderedText = Label.enableRightToLeftDetection ? reverseRtl(renderedValue) : renderedValue; @@ -1348,7 +1346,7 @@ Label.getScreenSpaceBoundingBox = function ( }; /** - * Removes control characters, which will cause an error when rendering a glyph. + * Removes control characters and soft hyphon (auto-wrap) characters, which will cause an error when rendering a glyph. This does not remove tabs, carriage returns, or newlines. * @private * @param {string} text The original label text * @returns {string} The renderable filtered text @@ -1356,7 +1354,7 @@ Label.getScreenSpaceBoundingBox = function ( Label.filterUnsupportedCharacters = function (text) { const problematicCharactersRegex = new RegExp( // eslint-disable-next-line no-control-regex - /[\u0000-\u001F\u202a-\u206f\u200b-\u200f]/, + /[\u0000-\u0008\u000E-\u001F\u00ad\u202a-\u206f\u200b-\u200f]/, "g", ); return text.replace(problematicCharactersRegex, ""); @@ -1518,7 +1516,7 @@ function reverseBrackets(bracket) { } } -//To add another language, simply add its Unicode block range(s) to the below regex. +// To add another language, add its Unicode block range(s) to the below regex. const hebrew = "\u05D0-\u05EA"; const arabic = "\u0600-\u06FF\u0750-\u077F\u08A0-\u08FF"; const rtlChars = new RegExp(`[${hebrew}${arabic}]`); diff --git a/packages/engine/Specs/Scene/LabelCollectionSpec.js b/packages/engine/Specs/Scene/LabelCollectionSpec.js index e4488420cba2..68c7d7dbf6dd 100644 --- a/packages/engine/Specs/Scene/LabelCollectionSpec.js +++ b/packages/engine/Specs/Scene/LabelCollectionSpec.js @@ -2627,8 +2627,8 @@ describe( }); it("filterUnsupportedCharacters removes unicode characters from text only if they cause render issues", function () { - const text = "test \u000A - with Line Feed (LF)"; - const expectedText = "test - with Line Feed (LF)"; + const text = "test \u000E - with Shift Out (SO)"; + const expectedText = "test - with Shift Out (SO)"; expect(Label.filterUnsupportedCharacters(text)).toEqual(expectedText); }); From 188e2bf3948fe95479f64fa6b5c6d287f01a4f18 Mon Sep 17 00:00:00 2001 From: jjspace <8007967+jjspace@users.noreply.github.com> Date: Wed, 20 Nov 2024 13:40:33 -0500 Subject: [PATCH 103/175] fix type generation --- packages/engine/Source/Core/ITwinPlatform.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/engine/Source/Core/ITwinPlatform.js b/packages/engine/Source/Core/ITwinPlatform.js index 734d2b150749..cbb8499aac0a 100644 --- a/packages/engine/Source/Core/ITwinPlatform.js +++ b/packages/engine/Source/Core/ITwinPlatform.js @@ -17,7 +17,6 @@ const ITwinPlatform = {}; /** * Status states for a mesh-export export * @enum {string} - * @private */ ITwinPlatform.ExportStatus = Object.freeze({ NotStarted: "NotStarted", @@ -29,7 +28,6 @@ ITwinPlatform.ExportStatus = Object.freeze({ /** * Types of mesh-export exports. CesiumJS only supports loading 3DTILES type exports * @enum {string} - * @private */ ITwinPlatform.ExportType = Object.freeze({ IMODEL: "IMODEL", From 3726464a6294ae1ddb2dcd486670f8b3aa10fbd0 Mon Sep 17 00:00:00 2001 From: jjspace <8007967+jjspace@users.noreply.github.com> Date: Wed, 20 Nov 2024 14:46:51 -0500 Subject: [PATCH 104/175] minor renaming and descriptions --- Apps/Sandcastle/gallery/iTwin Demo.html | 38 +++++------------------ packages/engine/Source/Scene/ITwinData.js | 6 ++-- 2 files changed, 10 insertions(+), 34 deletions(-) diff --git a/Apps/Sandcastle/gallery/iTwin Demo.html b/Apps/Sandcastle/gallery/iTwin Demo.html index 17e54c4f2294..29a95fe0747a 100644 --- a/Apps/Sandcastle/gallery/iTwin Demo.html +++ b/Apps/Sandcastle/gallery/iTwin Demo.html @@ -9,10 +9,10 @@ /> - - Cesium Demo + + iTwin iModel demo @@ -34,10 +34,11 @@ const { token } = await serviceResponse.json(); Cesium.ITwinPlatform.defaultAccessToken = token; + // TODO: remove/clean up when we pick the final sample iTwin we want to use // this is the iModel in the "Hello iTwinCesium" iTwin that we should all have access to // https://developer.bentley.com/my-itwins/b4a30036-0456-49ea-a439-3fcd9365e24e/home/ - // const imodelId = "2852c3d7-00c3-4b5d-a0ce-82bbde4f061e"; - const imodelId = "88673c1d-12b8-48f1-8beb-5000d0edbd0b"; + // const iModelId = "2852c3d7-00c3-4b5d-a0ce-82bbde4f061e"; + const iModelId = "88673c1d-12b8-48f1-8beb-5000d0edbd0b"; // Grabbed mapping from the iTwin Viewer const classes = { @@ -117,36 +118,11 @@ const statusOutput = document.querySelector("#status"); async function init() { statusOutput.innerText = "Starting export"; - const start = Date.now(); - const delay = (ms) => new Promise((res) => setTimeout(res, ms)); statusOutput.innerText = "Creating Tileset"; - let tileset; - try { - tileset = await Cesium.ITwinData.createTilesetFromModelId(imodelId); - } catch (error) { - if (!error.message.includes("not completed")) { - throw error; - } - statusOutput.innerText = "Tileset not completed, retrying..."; - - const timeoutAfter = 300000; - // wait until the export is complete - while (Date.now() - start > timeoutAfter) { - await delay(15000); - try { - tileset = await Cesium.ITwinData.createTilesetFromModelId(imodelId); - break; - } catch (error) { - if (!error.message.includes("not completed")) { - throw error; - } - } - } - throw new Cesium.RuntimeError("Export did not complete in time."); - } + const tileset = await Cesium.ITwinData.createTilesetFromIModelId(iModelId); scene.primitives.add(tileset); tileset.colorBlendMode = Cesium.Cesium3DTileColorBlendMode.REPLACE; diff --git a/packages/engine/Source/Scene/ITwinData.js b/packages/engine/Source/Scene/ITwinData.js index 0659620bdb13..70de32fe5e9f 100644 --- a/packages/engine/Source/Scene/ITwinData.js +++ b/packages/engine/Source/Scene/ITwinData.js @@ -10,7 +10,7 @@ import Check from "../Core/Check.js"; * * @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy. * - * @see ITwinPlatform to set the API token and base api url + * @see ITwinPlatform to set the API token and base API URL * @namespace ITwinData */ const ITwinData = {}; @@ -55,7 +55,7 @@ async function loadExport(exportObj, options) { * to re-attempt loading at a later time * * @example - * const tileset = await Cesium.ITwinData.createTilesetFromModelId(imodelId); + * const tileset = await Cesium.ITwinData.createTilesetFromIModelId(iModelId); * viewer.scene.primitives.add(tileset); * * @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy. @@ -67,7 +67,7 @@ async function loadExport(exportObj, options) { * @throws {RuntimeError} Wrong export type [type] * @throws {RuntimeError} Export is not completed. [id] - [status] */ -ITwinData.createTilesetFromModelId = async function (iModelId, options) { +ITwinData.createTilesetFromIModelId = async function (iModelId, options) { const { exports } = await ITwinPlatform.getExports(iModelId); const cesiumExport = exports[0]; if (!defined(cesiumExport)) { From a394dab7573cb160045b953d1299068c296ed6ab Mon Sep 17 00:00:00 2001 From: Connor Manning Date: Wed, 20 Nov 2024 14:21:47 -0600 Subject: [PATCH 105/175] Fix unit test. --- .../Specs/Scene/Model/PointCloudStylingPipelineStageSpec.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/engine/Specs/Scene/Model/PointCloudStylingPipelineStageSpec.js b/packages/engine/Specs/Scene/Model/PointCloudStylingPipelineStageSpec.js index 11cf22014bd7..ae7deeead20b 100644 --- a/packages/engine/Specs/Scene/Model/PointCloudStylingPipelineStageSpec.js +++ b/packages/engine/Specs/Scene/Model/PointCloudStylingPipelineStageSpec.js @@ -314,7 +314,9 @@ describe( "HAS_POINT_CLOUD_SHOW_STYLE", "COMPUTE_POSITION_WC_STYLE", ]); - ShaderBuilderTester.expectHasFragmentDefines(shaderBuilder, []); + ShaderBuilderTester.expectHasFragmentDefines(shaderBuilder, [ + "HAS_POINT_CLOUD_SHOW_STYLE", + ]); ShaderBuilderTester.expectHasVertexUniforms(shaderBuilder, [ "uniform vec4 model_pointCloudParameters;", From 785d9c9458e942645ec1604bd266774c7f1c8dd3 Mon Sep 17 00:00:00 2001 From: ggetz Date: Wed, 20 Nov 2024 16:18:34 -0500 Subject: [PATCH 106/175] Throttle total resources used for environment maps --- .../engine/Source/Renderer/ContextLimits.js | 2 +- .../Scene/DynamicEnvironmentMapManager.js | 89 +++++++++++++++++-- 2 files changed, 85 insertions(+), 6 deletions(-) diff --git a/packages/engine/Source/Renderer/ContextLimits.js b/packages/engine/Source/Renderer/ContextLimits.js index b6c5e5731f13..c435971762ac 100644 --- a/packages/engine/Source/Renderer/ContextLimits.js +++ b/packages/engine/Source/Renderer/ContextLimits.js @@ -44,7 +44,7 @@ Object.defineProperties(ContextLimits, { }, /** - * The approximate maximum cube mape width and height supported by this WebGL implementation. + * The approximate maximum cube map width and height supported by this WebGL implementation. * The minimum is 16, but most desktop and laptop implementations will support much larger sizes like 8,192. * @memberof ContextLimits * @type {number} diff --git a/packages/engine/Source/Scene/DynamicEnvironmentMapManager.js b/packages/engine/Source/Scene/DynamicEnvironmentMapManager.js index 40fd2560f1be..66adbe9231b1 100644 --- a/packages/engine/Source/Scene/DynamicEnvironmentMapManager.js +++ b/packages/engine/Source/Scene/DynamicEnvironmentMapManager.js @@ -12,6 +12,7 @@ import PixelFormat from "../Core/PixelFormat.js"; import SceneMode from "./SceneMode.js"; import Transforms from "../Core/Transforms.js"; import ComputeCommand from "../Renderer/ComputeCommand.js"; +import ContextLimits from "../Renderer/ContextLimits.js"; import CubeMap from "../Renderer/CubeMap.js"; import Framebuffer from "../Renderer/Framebuffer.js"; import Texture from "../Renderer/Texture.js"; @@ -79,7 +80,11 @@ function DynamicEnvironmentMapManager(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); - const mipmapLevels = defaultValue(options.mipmapLevels, 10); + const mipmapLevels = Math.min( + defaultValue(options.mipmapLevels, 10), + Math.log2(ContextLimits.maximumCubeMapSize), + ); + this._mipmapLevels = mipmapLevels; this._radianceMapComputeCommands = new Array(6); this._convolutionComputeCommands = new Array((mipmapLevels - 1) * 6); @@ -295,6 +300,62 @@ Object.defineProperties(DynamicEnvironmentMapManager.prototype, { }, }); +console.log(ContextLimits); + +// Internally manage a queue of commands across all instances to prevent too many commands from being added in a single frame and using too much memory at once. +DynamicEnvironmentMapManager._maximumComputeCommandCount = + Math.log2(ContextLimits.maximumCubeMapSize) * 6; // Scale relative to GPU resources available +DynamicEnvironmentMapManager._activeComputeCommandCount = 0; +DynamicEnvironmentMapManager._nextFrameCommandQueue = []; +/** + * Add a command to the queue. If possible, it will be added to the list of commands for the next frame. Otherwise, it will be added to a backlog + * and attempted next frame. + * @private + * @param {ComputeCommand} command The created command + * @param {FrameState} frameState The current frame state + */ +DynamicEnvironmentMapManager._queueCommand = (command, frameState) => { + if ( + DynamicEnvironmentMapManager._activeComputeCommandCount >= + DynamicEnvironmentMapManager._maximumComputeCommandCount + ) { + // Command will instead be scheduled next frame + DynamicEnvironmentMapManager._nextFrameCommandQueue.push(command); + return; + } + + frameState.commandList.push(command); + DynamicEnvironmentMapManager._activeComputeCommandCount++; +}; +/** + * If there are any backlogged commands, queue up as many as possible for the next frame. + * @private + * @param {FrameState} frameState The current frame state + */ +DynamicEnvironmentMapManager._updateCommandQueue = (frameState) => { + if ( + DynamicEnvironmentMapManager._nextFrameCommandQueue.length > 0 && + DynamicEnvironmentMapManager._activeComputeCommandCount < + DynamicEnvironmentMapManager._maximumComputeCommandCount + ) { + let command = DynamicEnvironmentMapManager._nextFrameCommandQueue.pop(); + while ( + defined(command) && + DynamicEnvironmentMapManager._activeComputeCommandCount < + DynamicEnvironmentMapManager._maximumComputeCommandCount + ) { + if (command.canceled) { + command = DynamicEnvironmentMapManager._nextFrameCommandQueue.pop(); + continue; + } + + frameState.commandList.push(command); + DynamicEnvironmentMapManager._activeComputeCommandCount++; + command = DynamicEnvironmentMapManager._nextFrameCommandQueue.pop(); + } + } +}; + /** * Sets the owner for the input DynamicEnvironmentMapManager if there wasn't another owner. * Destroys the owner's previous DynamicEnvironmentMapManager if setting is successful. @@ -334,15 +395,25 @@ DynamicEnvironmentMapManager.setOwner = function ( DynamicEnvironmentMapManager.prototype.reset = function () { let length = this._radianceMapComputeCommands.length; for (let i = 0; i < length; ++i) { + if (defined(this._radianceMapComputeCommands[i])) { + this._radianceMapComputeCommands[i].canceled = true; + DynamicEnvironmentMapManager._activeComputeCommandCount--; + } this._radianceMapComputeCommands[i] = undefined; } length = this._convolutionComputeCommands.length; for (let i = 0; i < length; ++i) { + if (defined(this._convolutionComputeCommands[i])) { + this._convolutionComputeCommands[i].canceled = true; + DynamicEnvironmentMapManager._activeComputeCommandCount--; + } this._convolutionComputeCommands[i] = undefined; } if (defined(this._irradianceComputeCommand)) { + this._irradianceComputeCommand.canceled = true; + DynamicEnvironmentMapManager._activeComputeCommandCount--; this._irradianceComputeCommand = undefined; } @@ -520,14 +591,17 @@ function updateRadianceMap(manager, frameState) { framebuffer._unBind(); framebuffer.destroy(); + DynamicEnvironmentMapManager._activeComputeCommandCount--; + if (!commands.some(defined)) { manager._convolutionsCommandsDirty = true; manager._shouldRegenerateShaders = true; } }, }); - frameState.commandList.push(command); + manager._radianceMapComputeCommands[i] = command; + DynamicEnvironmentMapManager._queueCommand(command, frameState); i++; } manager._radianceCommandsDirty = false; @@ -554,7 +628,7 @@ function updateSpecularMaps(manager, frameState) { const getPostExecute = (index, texture, face, level) => () => { // Copy output texture to corresponding face and mipmap level const commands = manager._convolutionComputeCommands; - if (!defined(commands[index])) { + if (!defined(commands[index]) || commands[index].canceled) { // This command was cancelled return; } @@ -562,6 +636,7 @@ function updateSpecularMaps(manager, frameState) { radianceCubeMap.copyFace(frameState, texture, face, level); facesCopied++; + DynamicEnvironmentMapManager._activeComputeCommandCount--; // All faces and levels have been copied if (facesCopied === manager._specularMapTextures.length) { @@ -619,7 +694,7 @@ function updateSpecularMaps(manager, frameState) { postExecute: getPostExecute(index, texture, face, level), }); manager._convolutionComputeCommands[index] = command; - frameState.commandList.push(command); + DynamicEnvironmentMapManager._queueCommand(command, frameState); ++index; } @@ -674,10 +749,12 @@ function updateIrradianceResources(manager, frameState) { manager._irradianceTextureDirty = false; manager._irradianceComputeCommand = undefined; manager._sphericalHarmonicCoefficientsDirty = true; + + DynamicEnvironmentMapManager._activeComputeCommandCount--; }, }); manager._irradianceComputeCommand = command; - frameState.commandList.push(command); + DynamicEnvironmentMapManager._queueCommand(command, frameState); manager._irradianceTextureDirty = true; } @@ -744,6 +821,8 @@ DynamicEnvironmentMapManager.prototype.update = function (frameState) { return; } + DynamicEnvironmentMapManager._updateCommandQueue(frameState); + const dynamicLighting = frameState.atmosphere.dynamicLighting; const regenerateEnvironmentMap = atmosphereNeedsUpdate(this, frameState) || From e503014a670051b92a403a95e9c92b5fb3b0b427 Mon Sep 17 00:00:00 2001 From: ggetz Date: Wed, 20 Nov 2024 16:43:03 -0500 Subject: [PATCH 107/175] Update CHANGES.md --- CHANGES.md | 1 + .../Source/Scene/DynamicEnvironmentMapManager.js | 11 +++++------ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 6553014c59d6..3b415a3cc96f 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -12,6 +12,7 @@ ##### Fixes :wrench: - Fix label rendering bug in WebGL1 contexts. [#12301](https://github.com/CesiumGS/cesium/pull/12301) +- Fixed lag or crashes when loading many models in the same frame. [#12320](https://github.com/CesiumGS/cesium/pull/12320) #### @cesium/widgets diff --git a/packages/engine/Source/Scene/DynamicEnvironmentMapManager.js b/packages/engine/Source/Scene/DynamicEnvironmentMapManager.js index 66adbe9231b1..5f4545301e09 100644 --- a/packages/engine/Source/Scene/DynamicEnvironmentMapManager.js +++ b/packages/engine/Source/Scene/DynamicEnvironmentMapManager.js @@ -300,11 +300,8 @@ Object.defineProperties(DynamicEnvironmentMapManager.prototype, { }, }); -console.log(ContextLimits); - // Internally manage a queue of commands across all instances to prevent too many commands from being added in a single frame and using too much memory at once. -DynamicEnvironmentMapManager._maximumComputeCommandCount = - Math.log2(ContextLimits.maximumCubeMapSize) * 6; // Scale relative to GPU resources available +DynamicEnvironmentMapManager._maximumComputeCommandCount = 8; // This value is updated once a context is created. DynamicEnvironmentMapManager._activeComputeCommandCount = 0; DynamicEnvironmentMapManager._nextFrameCommandQueue = []; /** @@ -686,7 +683,7 @@ function updateSpecularMaps(manager, frameState) { owner: manager, uniformMap: { u_roughness: () => level / (mipmapLevels - 1), - u_radianceTexture: () => radianceCubeMap, + u_radianceTexture: () => radianceCubeMap ?? context.defaultTexture, u_faceDirection: () => { return CubeMap.getDirection(face, scratchCartesian); }, @@ -739,7 +736,7 @@ function updateIrradianceResources(manager, frameState) { fragmentShaderSource: fs, outputTexture: texture, uniformMap: { - u_radianceMap: () => manager._radianceCubeMap, + u_radianceMap: () => manager._radianceCubeMap ?? context.defaultTexture, }, postExecute: () => { if (!defined(manager._irradianceComputeCommand)) { @@ -821,6 +818,8 @@ DynamicEnvironmentMapManager.prototype.update = function (frameState) { return; } + DynamicEnvironmentMapManager._maximumComputeCommandCount = + Math.log2(ContextLimits.maximumCubeMapSize) * 6; // Scale relative to GPU resources available DynamicEnvironmentMapManager._updateCommandQueue(frameState); const dynamicLighting = frameState.atmosphere.dynamicLighting; From 3939f57a63d50ce5f11c9785c81b123f2cb8b14c Mon Sep 17 00:00:00 2001 From: Jeshurun Hembd Date: Wed, 20 Nov 2024 19:07:53 -0500 Subject: [PATCH 108/175] Add stepCount and directionCount uniforms to AmbientOcclusion --- .../Source/Scene/PostProcessStageLibrary.js | 18 ++++++++ .../AmbientOcclusionGenerate.glsl | 41 ++++++++++++++----- 2 files changed, 48 insertions(+), 11 deletions(-) diff --git a/packages/engine/Source/Scene/PostProcessStageLibrary.js b/packages/engine/Source/Scene/PostProcessStageLibrary.js index f4329daee77f..1f21f1ab2961 100644 --- a/packages/engine/Source/Scene/PostProcessStageLibrary.js +++ b/packages/engine/Source/Scene/PostProcessStageLibrary.js @@ -509,6 +509,8 @@ PostProcessStageLibrary.createAmbientOcclusionStage = function () { intensity: 3.0, bias: 0.1, lengthCap: 0.26, + directionCount: 16, + stepCount: 64, stepSize: 1.95, frustumLength: 1000.0, randomTexture: undefined, @@ -556,6 +558,22 @@ PostProcessStageLibrary.createAmbientOcclusionStage = function () { generate.uniforms.lengthCap = value; }, }, + directionCount: { + get: function () { + return generate.uniforms.directionCount; + }, + set: function (value) { + generate.uniforms.directionCount = value; + }, + }, + stepCount: { + get: function () { + return generate.uniforms.stepCount; + }, + set: function (value) { + generate.uniforms.stepCount = value; + }, + }, stepSize: { get: function () { return generate.uniforms.stepSize; diff --git a/packages/engine/Source/Shaders/PostProcessStages/AmbientOcclusionGenerate.glsl b/packages/engine/Source/Shaders/PostProcessStages/AmbientOcclusionGenerate.glsl index 6e6c2e0e0152..5b8f7c6010dd 100644 --- a/packages/engine/Source/Shaders/PostProcessStages/AmbientOcclusionGenerate.glsl +++ b/packages/engine/Source/Shaders/PostProcessStages/AmbientOcclusionGenerate.glsl @@ -7,6 +7,8 @@ uniform float bias; uniform float lengthCap; uniform float stepSize; uniform float frustumLength; +uniform int stepCount; +uniform int directionCount; vec3 pixelToEye(vec2 screenCoordinate) { @@ -58,17 +60,17 @@ void main(void) } vec3 normalEC = getNormalXEdge(positionEC); - float samplingRadius = lengthCap * sqrt(-positionEC.z); + float gaussianVariance = lengthCap * sqrt(-positionEC.z); + // TODO: mix of units. Steps are in pixels; variance is in meters. + //float stepLength = max(1.0, 3.0 * gaussianVariance / (float(stepCount) + 1.0)); - const int ANGLE_STEPS = 16; - float angleStepScale = 1.0 / float(ANGLE_STEPS); + float angleStepScale = 1.0 / float(directionCount); float angleStep = angleStepScale * czm_twoPi; float cosStep = cos(angleStep); float sinStep = sin(angleStep); mat2 rotateStep = mat2(cosStep, sinStep, -sinStep, cosStep); - const int RADIAL_STEPS = 64; - float radialStepScale = 1.0 / float(RADIAL_STEPS); + float radialStepScale = 1.0 / float(stepCount); // Initial sampling direction (different for each pixel) const float randomTextureSize = 255.0; @@ -78,16 +80,33 @@ void main(void) float ao = 0.0; // Loop over sampling directions - for (int i = 0; i < ANGLE_STEPS; i++) +//#if __VERSION__ == 300 +// for (int i = 0; i < directionCount; i++) +// { +//#else + for (int i = 0; i < 64; i++) { + if (i >= directionCount) { + break; + } +//#endif sampleDirection = rotateStep * sampleDirection; float localAO = 0.0; float accumulatedWindowWeights = 0.0; + //vec2 radialStep = stepLength * sampleDirection; vec2 radialStep = stepSize * sampleDirection; - for (int j = 0; j < RADIAL_STEPS; j++) +//#if __VERSION__ == 300 +// for (int j = 0; j < stepCount; j++) +// { +//#else + for (int j = 0; j < 128; j++) { + if (j >= stepCount) { + break; + } +//#endif // Step along sampling direction, away from output pixel vec2 newCoords = floor(gl_FragCoord.xy + float(j + 1) * radialStep) + vec2(0.5); @@ -109,14 +128,14 @@ void main(void) } // Weight contribution based on the distance from the output point - float stepLength = length(stepVector); - float weight = gaussian(stepLength, samplingRadius); + float sampleDistance = length(stepVector); + float weight = gaussian(sampleDistance, gaussianVariance); localAO += weight * dotVal; // Compute lateral distance from output point, for weight normalization // TODO: This is slow! Better to analytically compute window scales - float windowLength = length(stepPositionEC.xy - positionEC.xy); - accumulatedWindowWeights += gaussian(windowLength, samplingRadius); + float lateralDistance = length(stepPositionEC.xy - positionEC.xy); + accumulatedWindowWeights += gaussian(lateralDistance, gaussianVariance); } ao += 24.0 * localAO / accumulatedWindowWeights; } From 8a84e304da4e870c3c1e3329e387384e56d431fd Mon Sep 17 00:00:00 2001 From: Jeshurun Hembd Date: Wed, 20 Nov 2024 19:18:17 -0500 Subject: [PATCH 109/175] Fix AmbientOcclusion stepCount scaling, update WebGL2 loops --- .../AmbientOcclusionGenerate.glsl | 26 +++++++++---------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/packages/engine/Source/Shaders/PostProcessStages/AmbientOcclusionGenerate.glsl b/packages/engine/Source/Shaders/PostProcessStages/AmbientOcclusionGenerate.glsl index 5b8f7c6010dd..74104993ec1d 100644 --- a/packages/engine/Source/Shaders/PostProcessStages/AmbientOcclusionGenerate.glsl +++ b/packages/engine/Source/Shaders/PostProcessStages/AmbientOcclusionGenerate.glsl @@ -70,8 +70,6 @@ void main(void) float sinStep = sin(angleStep); mat2 rotateStep = mat2(cosStep, sinStep, -sinStep, cosStep); - float radialStepScale = 1.0 / float(stepCount); - // Initial sampling direction (different for each pixel) const float randomTextureSize = 255.0; vec2 randomTexCoord = fract(gl_FragCoord.xy / randomTextureSize); @@ -80,16 +78,16 @@ void main(void) float ao = 0.0; // Loop over sampling directions -//#if __VERSION__ == 300 -// for (int i = 0; i < directionCount; i++) -// { -//#else +#if __VERSION__ == 300 + for (int i = 0; i < directionCount; i++) + { +#else for (int i = 0; i < 64; i++) { if (i >= directionCount) { break; } -//#endif +#endif sampleDirection = rotateStep * sampleDirection; float localAO = 0.0; @@ -97,16 +95,16 @@ void main(void) //vec2 radialStep = stepLength * sampleDirection; vec2 radialStep = stepSize * sampleDirection; -//#if __VERSION__ == 300 -// for (int j = 0; j < stepCount; j++) -// { -//#else +#if __VERSION__ == 300 + for (int j = 0; j < stepCount; j++) + { +#else for (int j = 0; j < 128; j++) { if (j >= stepCount) { break; } -//#endif +#endif // Step along sampling direction, away from output pixel vec2 newCoords = floor(gl_FragCoord.xy + float(j + 1) * radialStep) + vec2(0.5); @@ -137,10 +135,10 @@ void main(void) float lateralDistance = length(stepPositionEC.xy - positionEC.xy); accumulatedWindowWeights += gaussian(lateralDistance, gaussianVariance); } - ao += 24.0 * localAO / accumulatedWindowWeights; + ao += localAO / accumulatedWindowWeights; } - ao *= angleStepScale * radialStepScale; + ao *= angleStepScale; ao = 1.0 - clamp(ao, 0.0, 1.0); ao = pow(ao, intensity); out_FragColor = vec4(vec3(ao), 1.0); From 2ca3ae7aac6e139da3249d8e03d296bf5c60a428 Mon Sep 17 00:00:00 2001 From: "jerome.fayot" Date: Tue, 19 Nov 2024 07:23:23 +0100 Subject: [PATCH 110/175] feat: added TrackingReferenceFrame.ENU --- Apps/Sandcastle/gallery/Entity tracking.html | 7 +++++++ Apps/Sandcastle/gallery/Interpolation.html | 2 +- .../engine/Source/Core/TrackingReferenceFrame.js | 9 +++++++++ packages/engine/Source/DataSources/EntityView.js | 10 ++++++---- .../engine/Specs/DataSources/EntityViewSpec.js | 15 +++++++++++++++ 5 files changed, 38 insertions(+), 5 deletions(-) diff --git a/Apps/Sandcastle/gallery/Entity tracking.html b/Apps/Sandcastle/gallery/Entity tracking.html index f5f21aa22a47..6e894c642e9d 100644 --- a/Apps/Sandcastle/gallery/Entity tracking.html +++ b/Apps/Sandcastle/gallery/Entity tracking.html @@ -94,6 +94,13 @@ drone.trackingReferenceFrame = Cesium.TrackingReferenceFrame.VELOCITY; }, }, + { + text: "Tracking reference frame: East-North-Up", + onselect: function () { + satellite.trackingReferenceFrame = Cesium.TrackingReferenceFrame.ENU; + drone.trackingReferenceFrame = Cesium.TrackingReferenceFrame.ENU; + }, + }, ]); //Sandcastle_End }; diff --git a/Apps/Sandcastle/gallery/Interpolation.html b/Apps/Sandcastle/gallery/Interpolation.html index 567ec12627ea..f21a5dd259c3 100644 --- a/Apps/Sandcastle/gallery/Interpolation.html +++ b/Apps/Sandcastle/gallery/Interpolation.html @@ -158,7 +158,7 @@ { text: "Tracking reference frame: East-North-Up", onselect: function () { - entity.trackingReferenceFrame = Cesium.TrackingReferenceFrame.EAST_NORTH_UP; + entity.trackingReferenceFrame = Cesium.TrackingReferenceFrame.ENU; }, }, { diff --git a/packages/engine/Source/Core/TrackingReferenceFrame.js b/packages/engine/Source/Core/TrackingReferenceFrame.js index 4ad418e96b7a..a7e735b8e1e5 100644 --- a/packages/engine/Source/Core/TrackingReferenceFrame.js +++ b/packages/engine/Source/Core/TrackingReferenceFrame.js @@ -36,5 +36,14 @@ const TrackingReferenceFrame = { * @constant */ VELOCITY: 2, + + /** + * The entity's local East-North-Up reference frame. + * When selected, the auto-detect algorithm is overridden. + * + * @type {number} + * @constant + */ + ENU: 3, }; export default Object.freeze(TrackingReferenceFrame); diff --git a/packages/engine/Source/DataSources/EntityView.js b/packages/engine/Source/DataSources/EntityView.js index bc759b53dbd6..9e7ae33234b5 100644 --- a/packages/engine/Source/DataSources/EntityView.js +++ b/packages/engine/Source/DataSources/EntityView.js @@ -269,7 +269,12 @@ function updateTransform( rotationScratch, ); Matrix4.fromRotationTranslation(rotation, cartesian, transform); - } else if (hasBasis) { + } else if ( + trackingReferenceFrame === TrackingReferenceFrame.ENU || + !hasBasis + ) { + Transforms.eastNorthUpToFixedFrame(cartesian, ellipsoid, transform); + } else { transform[0] = xBasis.x; transform[1] = xBasis.y; transform[2] = xBasis.z; @@ -286,9 +291,6 @@ function updateTransform( transform[13] = cartesian.y; transform[14] = cartesian.z; transform[15] = 0.0; - } else { - // Stationary or slow-moving, low-altitude objects use East-North-Up. - Transforms.eastNorthUpToFixedFrame(cartesian, ellipsoid, transform); } camera._setTransform(transform); diff --git a/packages/engine/Specs/DataSources/EntityViewSpec.js b/packages/engine/Specs/DataSources/EntityViewSpec.js index 2aa12913f9e5..61223cc1dd0b 100644 --- a/packages/engine/Specs/DataSources/EntityViewSpec.js +++ b/packages/engine/Specs/DataSources/EntityViewSpec.js @@ -83,6 +83,10 @@ describe( entity.trackingReferenceFrame = TrackingReferenceFrame.VELOCITY; view.update(JulianDate.now()); expect(view.scene.camera.position).toEqualEpsilon(sampleOffset, 1e-10); + + entity.trackingReferenceFrame = TrackingReferenceFrame.ENU; + view.update(JulianDate.now()); + expect(view.scene.camera.position).toEqualEpsilon(sampleOffset, 1e-10); }); it("uses entity bounding sphere", function () { @@ -115,6 +119,13 @@ describe( new BoundingSphere(new Cartesian3(3, 4, 5), 6), ); expect(view.scene.camera.position).toEqualEpsilon(sampleOffset, 1e-10); + + entity.trackingReferenceFrame = TrackingReferenceFrame.ENU; + view.update( + JulianDate.now(), + new BoundingSphere(new Cartesian3(3, 4, 5), 6), + ); + expect(view.scene.camera.position).toEqualEpsilon(sampleOffset, 1e-10); }); it("uses entity viewFrom if available and boundingsphere is supplied", function () { @@ -140,6 +151,10 @@ describe( entity.trackingReferenceFrame = TrackingReferenceFrame.VELOCITY; view.update(JulianDate.now()); expect(view.scene.camera.position).toEqualEpsilon(sampleOffset, 1e-10); + + entity.trackingReferenceFrame = TrackingReferenceFrame.ENU; + view.update(JulianDate.now()); + expect(view.scene.camera.position).toEqualEpsilon(sampleOffset, 1e-10); }); it("update throws without time parameter", function () { From 2715500bf1981e9aeae4595d9c98b342c70f5411 Mon Sep 17 00:00:00 2001 From: "jerome.fayot" Date: Thu, 21 Nov 2024 07:20:05 +0100 Subject: [PATCH 111/175] doc: updated CHANGES.md and TrackingReferenceFrame documentation --- CHANGES.md | 6 +++++- .../Source/Core/TrackingReferenceFrame.js | 20 ++++++++----------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 6553014c59d6..8b86cea1cf1d 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -7,7 +7,11 @@ ##### Additions :tada: - Added `getSample` to `SampledProperty` to get the time of samples. [#12253](https://github.com/CesiumGS/cesium/pull/12253) -- Added `Entity.trackingReferenceFrame` property to allow tracking entities in their own inertial reference frame. [#12194](https://github.com/CesiumGS/cesium/pull/12194) +- Added `Entity.trackingReferenceFrame` property to allow tracking entities in various reference frames. [#12194](https://github.com/CesiumGS/cesium/pull/12194), [#12314](https://github.com/CesiumGS/cesium/pull/12314) + - `TrackingReferenceFrame.AUTODETECT` (default): uses either VVLH or ENU dependeding on entity's dynamic. Use `TrackingReferenceFrame.ENU` if your camera orientation flips abruptly from time to time. + - `TrackingReferenceFrame.ENU`: uses the entity's local East-North-Up reference frame. + - `TrackingReferenceFrame.INERTIAL`: uses the entity's inertial reference frame. + - `TrackingReferenceFrame.VELOCITY`: uses entity's `VelocityOrientationProperty` as orientation. ##### Fixes :wrench: diff --git a/packages/engine/Source/Core/TrackingReferenceFrame.js b/packages/engine/Source/Core/TrackingReferenceFrame.js index a7e735b8e1e5..d16be08b853b 100644 --- a/packages/engine/Source/Core/TrackingReferenceFrame.js +++ b/packages/engine/Source/Core/TrackingReferenceFrame.js @@ -17,33 +17,29 @@ const TrackingReferenceFrame = { AUTODETECT: 0, /** - * The entity's inertial reference frame. If entity has no defined orientation - * property, a {@link VelocityOrientationProperty} is used instead, thus - * falling back to TrackingReferenceFrame.VELOCITY. - * When selected, the auto-detect algorithm is overridden. + * The entity's local East-North-Up reference frame. * * @type {number} * @constant */ - INERTIAL: 1, + ENU: 1, /** - * The entity's inertial reference frame with orientation fixed to its - * {@link VelocityOrientationProperty}, ignoring its own orientation. - * When selected, the auto-detect algorithm is overridden. + * The entity's inertial reference frame. If entity has no defined orientation + * property, it falls back to auto-detect algorithm. * * @type {number} * @constant */ - VELOCITY: 2, + INERTIAL: 2, /** - * The entity's local East-North-Up reference frame. - * When selected, the auto-detect algorithm is overridden. + * The entity's inertial reference frame with orientation fixed to its + * {@link VelocityOrientationProperty}, ignoring its own orientation. * * @type {number} * @constant */ - ENU: 3, + VELOCITY: 3, }; export default Object.freeze(TrackingReferenceFrame); From a8665ca56c7c5542136868bf580278fa70249993 Mon Sep 17 00:00:00 2001 From: Emanuele Mastaglia Date: Thu, 21 Nov 2024 14:03:50 +0100 Subject: [PATCH 112/175] docs: update WMS URL in jsdoc of UrlTemplateImageryProvider --- .../engine/Source/Scene/UrlTemplateImageryProvider.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/engine/Source/Scene/UrlTemplateImageryProvider.js b/packages/engine/Source/Scene/UrlTemplateImageryProvider.js index 48dcab645f46..e11c00dfa138 100644 --- a/packages/engine/Source/Scene/UrlTemplateImageryProvider.js +++ b/packages/engine/Source/Scene/UrlTemplateImageryProvider.js @@ -159,13 +159,13 @@ const pickFeaturesTags = combine(tags, { * }); * // Access a Web Map Service (WMS) server. * const wms = new Cesium.UrlTemplateImageryProvider({ - * url : 'https://programs.communications.gov.au/geoserver/ows?tiled=true&' + - * 'transparent=true&format=image%2Fpng&exceptions=application%2Fvnd.ogc.se_xml&' + - * 'styles=&service=WMS&version=1.1.1&request=GetMap&' + - * 'layers=public%3AMyBroadband_Availability&srs=EPSG%3A3857&' + + * url : 'https://services.ga.gov.au/gis/services/NM_Hydrology_and_Marine_Points/MapServer/WMSServer?' + + * 'tiled=true&transparent=true&format=image%2Fpng&exceptions=application%2Fvnd.ogc.se_xml&' + + * 'styles=&service=WMS&version=1.3.0&request=GetMap&' + + * 'layers=Bores&crs=EPSG%3A3857&' + * 'bbox={westProjected}%2C{southProjected}%2C{eastProjected}%2C{northProjected}&' + * 'width=256&height=256', - * rectangle : Cesium.Rectangle.fromDegrees(96.799393, -43.598214999057824, 153.63925700000001, -9.2159219997013) + * rectangle : Cesium.Rectangle.fromDegrees(95.0, -55.0, 170.0, -1.0) // From GetCapabilities EX_GeographicBoundingBox * }); * // Using custom tags in your template url. * const custom = new Cesium.UrlTemplateImageryProvider({ From cae3a48348df0847b09e67c1da250ce7c45a7d03 Mon Sep 17 00:00:00 2001 From: Emanuele Mastaglia Date: Thu, 21 Nov 2024 14:22:36 +0100 Subject: [PATCH 113/175] chore: update CHANGES.md --- CHANGES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.md b/CHANGES.md index 6553014c59d6..828bf56fcfe7 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -12,6 +12,7 @@ ##### Fixes :wrench: - Fix label rendering bug in WebGL1 contexts. [#12301](https://github.com/CesiumGS/cesium/pull/12301) +- Updated WMS example URL in UrlTemplateImageryProvider documentation to use an active service. [#12323](https://github.com/CesiumGS/cesium/pull/12323) #### @cesium/widgets From d67aa3113e251fd17ac19a3b9ba600adbf37dce7 Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Thu, 21 Nov 2024 20:50:35 +0100 Subject: [PATCH 114/175] Remove outdated sandcastle from development section --- .../development/NGA GPM Visualization.html | 1378 ----------------- .../development/NGA GPM Visualization.jpg | Bin 8429 -> 0 bytes 2 files changed, 1378 deletions(-) delete mode 100644 Apps/Sandcastle/gallery/development/NGA GPM Visualization.html delete mode 100644 Apps/Sandcastle/gallery/development/NGA GPM Visualization.jpg diff --git a/Apps/Sandcastle/gallery/development/NGA GPM Visualization.html b/Apps/Sandcastle/gallery/development/NGA GPM Visualization.html deleted file mode 100644 index 86f84132ba5c..000000000000 --- a/Apps/Sandcastle/gallery/development/NGA GPM Visualization.html +++ /dev/null @@ -1,1378 +0,0 @@ - - - - - - - - - Cesium Demo - - - - - - - -
-
-

Loading...

-
-
- - - - - - - - - - - - - - - - - - - - - - - -
-

Cesium GPM Visualization

-
Data set - - - -
Anchor point ellipsoid - - - -
Shader mode - -
-
- - - diff --git a/Apps/Sandcastle/gallery/development/NGA GPM Visualization.jpg b/Apps/Sandcastle/gallery/development/NGA GPM Visualization.jpg deleted file mode 100644 index 5b2daea52ce03c25b298593a0d15c500b66cbe60..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8429 zcmb7o=QrG4*!6D=MrU*}`iK(TQKFXw87&cns1dyrql-?|j54E(UPGey5+Ul0E}{<6 z5)nOmi`TQB_51C|8LxM0Z$54PGP{-iok-{@3FF4E{$102AI`#BEU&3fvmp8bS!c|C`~K z4Fz+F64Da=k3O21j$RKLRv=b8v487va5E23+!AjoP(TSd>r9->QNFm_HnC%enft5z zwzm9U8kiG3_V)U-z*g{}PnvUt%Q$_CAWQ+T30Oz2HxWN$$fLFwDXVq<%|6hovr}8e z03(81JHu`O(vstsvTIsd(r&qnncw_&ZH)mI9>Vf2d9Mm*16RX5pHmi2p1o=L@!H-G0nVrsYDvyft5xRumEXvWi8^9}9usuTnsDTXLnb?ksR&tcn zE&WaNBuOj8JqeybsxO30j=Pw8wZBuYA*d>6YytNr0qS$2G}e0at7KpssKdDU67Ftg z!>I0aFgH zyuO#%dlpspVt;-#X%5WDG5)Evzd>DXfzZXq9@qRRA}lvo|2gj$-yQ^s^k2RWjR7IK zShQ^Re*OfiAp442W|TQpG+fc#U{F}WAiqk*%eqxzaeEpEc15PifP21`I{TP^TUsz_ zGd&s7!ziNay4GFBI^y!qMt1&Eds-89ZpyhKtf~ zE{r#TfYy_rMouOc?aqhu@4v(dh0xNmz^qXTGD<@J$Cc#%t{Y+T1CEXKNudD^jY8rL zberP`M$NJ>au4YF*BMqcmhG2NBpUh{+@q_P!K>}^Dsh_?+p_LYNPZDa#3Z(rH{it# zYsTfQk=8{c#gwv>C!#V)!#+#rLayu_?}bxX-huFUhHAtcZNcvri2xPn&s$jY}Wt- z8kFE*t@ZwpAiDciI2uPgOfA#BEO2q@nGa$zA^PIq?@I3hl!a2tKiRhe|^wEvT0PA`M};(RDrg zhZ`#}d*)utyYABc!jUwKhFG85o;o>^XKG`i*LY!5vU!ZM%%P|~w7})P@72SIvo}Y4 zd9PC)UOvca9QK_3BWe!iWF=}qQ`j702g;V3o$!modeeANGA>B$qqA0&;kextU+cIP zqSMbZdf${`Av=4w8x|N9B74Hv#8DscWzMy~z_W7xZmD&fBiy;vG-Fpa^brh63~uMC zjzEv9r4M;^&VMecSYXv4#+o3!Q`qJJFCLi1_12q#lXtd4v245VY&k6$vP}KfrP*UscP*#9HNfqKw)S8x< z-9eZ56}9+*xv+c(sBnKC_J+QO#TsLC2k#sjO*^5kAt&wS4|6Qc{Un>e)l!`;ev(C` z#+B8+driDCb9ohf1CTRLdS1yN3{;mW6`TA?UdZ%vR-D0>Wm0=g*Vf;aKWs*ACOPIZ z)4pyNR%DWWcmISbN8xwb0>4t#BZ42V$)cPZwjEw~5X-&)v@~>LTBp)1YcR55FXwhb z2_Mi2a_U}3MbK@-AGTxnB!6{cOMi3JcpDQJ%oT3D!^G*}8x`N+9?&yx!NGL0Duu z1}!7Psr*OmE6d6sFE}+(z5eM?Vw`H)8l`-7clyhKlg^&Yjd&$jzjMo(d690K7O*AB^ z;AzZ$CJF_PRkBFzC?Cizn35JpIMwi1v`~&8Z;YmVOU3jH+`R`^&BJeC#770%7o_K< zvzXIpv?V{3+eI3L&6Y`l5-z)rH!!_-}zr;%XDC6|B16r&_jL6QiS?Mx;FUx5_ zb4Im)@>jo#SESDlj}Y^UcHrN@KKQ(1zFXeu<*A~x)ub=(9LtWFzulkK!}m0i0G`<4+|D<_jCP`z*u)j7AnOl~+wVeLkr4m0fbMSj z^N8)>DF+L26W`d{ETxOkT*MpLH9upmTedpq)1o42tm0YOT_#g%Pj+23A!4POtb?}b znievji^`{r7<7v2m;MaSY@6Dxhq=7J)bSp(^N%1$W$UrZr<3vR8sMIp1zc1V-Ceon z*A~dXCC(R-v~j#P`(K&ulgN>Wzur+T?!ao>6}J!8`I`ywwD0f64iJitDos|!MTz5G zyUoPy39tV!&8h2Y6{`<;po|gpog^HZrmMSO|E8UtJkk!aFM6~@K2mQjcSj7gk#CvB z67NQD8e?Fq+jDoVolRrwr{4S7rQ~CsXnLZls?$T5#sFoFO<=9@o5*6{W?@e_ikF9Q zjq{rS+n+mDRt)Gg9ryL@@~f7@R8E^}NQrmWgFu;`Pt1w?Ttv|lHg9(+B8Z=QDfgaE*U=!ptxUY({)Ekyf2WBh=e7a;*&|NO}yH38U z(yp_7m)Hv%1HxGT*vMB2$yfYeuzC?oQWZBqihkLwoIp#dMNQpqnz40i=6N?Rv6SD# zv8e~in#{tOi1AGHX0{^&c zhY~Aj2tLUMHLQt7G>>F9eYnWmdXe=NYfVbx&&nf~FBjD3g{CmIVF8jVo^iRIesX!zGScSeZ6PsxnWa2RmqTfUg5b6Wi~y}G*O5ZfEus+pU8f3wfA`@_~=Z`*W5 z1dPs~3x3tCQ>lvh(UI_1U@=j!4#J|?F=4{ZkgdUbm~J35P!(9A|NRfM7HpMzk)}IC z>&HO(tKRaE@}-x882x8*ViAADv?&Q4v3Eqac=$PD*OE4NomPv z_jay?7(Q!IkRJUj{peHo74MFsK@qxD|BvhxoAA`R^$Bxdg7;OXX`2N#jGHRu3Z zf8Za|F&g8%4-fIO*X3FPu^1lY{^UAA!Ia;_ zicqPyhl`BMVzLO=E531P`;Yl|o^>Seov~3$p>$z8*F@tDNv^S3b@-~;~o-hhj73(qR|%<=oxc6MoN{hMT7Bdvnz&C7j4h{L9fYUU?pst#A?LP;YM7u~K7Cy`K>qT_pe~*v zw#QC$o6{dHDjsazwc#~<^M%uzFp0YQ=W&&keaS8=j}}EN?c#OXeFOMcvdPY9*VM%^ z@OdPZJRDua9-cnf^xJ5V@J?Z-d~OsG&Mll7nU$8^u*yhpn*)*_8;TPQox>YEL;w2B zSisQuwb~m-Yw1HuA`o%3W$fF8G_BaipD!nQfR3u!A5eZklB<+6o~z6A>dUrRZadV% z<;aG_5d)V+aXE9gga3uy0IvxuX`kIIz8|x}mwyixI3gp7d$B>HMEUv#c(1hnK;Q=0 z9?`l1xR~(&8U=z5gI~`XLw;V*$bDl55rh*XyU#rw9m!HP;PE)|vy1DFNQsq8RHP_4 zrzoFKG3P1Oa(^hxFh=TSWSd^YB8m3Phm#8k?uWG%6jX0F*pDiViyo@d-WwKeci*p` zI-d>@*X)XP5B)2Sj{Ba|`x|YHg8pOX84G_(FmoTI^n-s;fX-ROa21AdcV~rP6InRF z^Oel!RL?F9Ta3@Z|JaT@rgIn0SUmG=DddFa7fH zBckuukhfQydYr)OSmTx&3&#v$;p^EK>*IE|rU_@QnHut3@`-QGS9p*VUuRWqU+xhm zUUoqrUa3_7ywEHCyK?IWm#6|E?w!vbqeq`dyFjAKy0her#r)$Qb1L zgav9t-cf*+z?pVluiNYw@B1IKIW|a6%*TeNiD%OjwJ2t88Xw|%sp-VS8r4dWu*%MU zJNb125AnZ+j)U8wUVLsl`3}R_*Fwg?F8;o_mc)Oo;MeZSbW>tSM_W(SeWT10!mYLE7vGngbKz7{)hzeA2!noCE_Mh`ews+oo?;PF(5SSX zu&a2O{n62xkt|cHE+S(hog%N~#4_b~9+7BlJ%L_DpHnQEV@`o3<-SF_!rH8f_K5y?GkG=_V zPBvZaVV~?i&1QV@TWM;>($Fvb5xg(L%QI}CtWu&YN#~*f&lO*g%Beg_Spkk{9%3%4 z#hCME8mJG65bH&k3->~MCkM-9I6tspw+2!Ldq3B@v+#fktNdjS%RWCORq~=1;PUae z9`kk6U$-oFvAMj|8(psgN%xaO4IQvgFO8Y1SH{?XWkePweD5Hcv<|1IRr+!GQE0*@ zQ_I#6jtG0EU3LRxCAX(_>zN@@0Ayh_6IGlpl-?WBkkgGk*DD}!Qxtgw)bu+<1bk?x zw`uxeUVH=as5(p|jJ`hv7mTg`OYwuJVx(74g5_}k^^1+k1j0}67>c*y=GyArmyvKK(+sp>AjicHcq!FV$@}rnaL*S1Vqs2c-E3F(9yaG@ItkP4dQll8gVup-{Dd7% zit!V{4@I6eEcn3MJd+VUkt*XK{MormZ{O%TP>D|vSm91;!!8G`*y@|X(uxhnDOWHg zg&X>m{|0EFDovX5HZ^s88?=DsK1R_HBy#2bmr6GFGU3-9>sSuxmqc2z5ynma!>gk63Bm$W1Egp^r7pmZ-SK+Kl^KCZ{dbLYZzn|9TmMVN@TqJGaJZX(jgTz)3}@vGfJE|JY=ri{hqF?~L6?<4_4j$-iXD zw$)ur(%8=$wAPs^t~!3BRcO`kF2ZuTHNANQaqgsaVFGD z%0t{jDAulm8U-1N&qv~Tr66_~I|EZiVk(DL)4Nu5)jJFJhyB3$#YE`zlLAcEnRDw} zf@BLP%GKxs^yjI_Dfhfs)v9s#;K1XH=}7#+JKmT!dbo~A?v&Nxhut`Mz1U(%34Mj zi*R?v;Nv2vDqZVV46_M8zQtu-iif>XBU7@srez5SUbbfNv+BS=(j;QaR8RzJB_76W zsTncDREEp|jic&Qv^bMl10r=nghUq`95P$QJdA<)_4{#JaE$A^l=#Nx>kG+Ng9$gv zkLl;)e#*62iF7PyQ#r`|tWqTL~VQg?yJ?5R@pvP zi1uf}1lrI_vmcdI_Ud(D7$4`1BKF-C5g%Q0t>Apz!UBFjV-ws=%#YYv+QVIm=9 zPeQ$@MRKyS1eM%Ml$Jr#Mq=0Sw+&F`zuyh#B@JmSB)=qRIr_45 zLFd;`UA{@at030TfCpa(3gVxr8&MWG+xhbZDSD?P#e5?iv4rQB0+$A5;}0^{cWu8_ z@wmQ^TIaMxVib)_IXgQomo$PrTe4AuZzv8=`F3vyI2^%ezMos|$T1@V@_Cg(73p-V zsAA^DBzcArAxni9gVDM*6PBh6_hNXHzy4(&g(R`I+1voK#?O3I1Ox*!=#n)y>t5n# zA+0T0FGq(9*5RPMXC)4gB!UK`{7yey!Mzy{M!9h=Q}W`ixpSLqb-%y;BG42vdHdUA zh40nU6xSb~lZ#HAa5Q}pgVD<*Hoe#t~RasZT!7{}lN`oT3 z5FhOzN+aBT!XyE}0@{6fKHkhxDNQ7}i&c24`RnAk9PvkZXu+FU#`N^BoO}My==n$> zlXo}}@rZ(8Ic#I(&epT3>F*DU?0uR%J82Qm8#~p83c3$}rQ}+|-nPXLoQbcz$}9iW zpF9sX6Z?cGP09)R7sX(QTpgYGUi(v*?LQ02lTDatDES@>AO3}k`YF{27vx^|rV4M) zI~g7n^_%c$PcPpKo~4~Q`#9Q!qO*Yp{^+#+ItB6N-CCESeG z@{8m}u_6l|#*hOj>ITp(cI54S+n$i~ELWf}Qdvy-&tw8K@%mLovn(A*Fj?C*2W_cW z1d3rEF=sPEKs>2PIQl&wXO!l-M)zU{MH_PSWXEU*JQaDgH-UrfU2=m4(N#0yMNtvQ zrsva>z7q|npkk{);n3L&1EP|S;ZajEe+X&=ua`&Z30kB}9zz%P$T-Z2MEiKinfec+sm<9TqtFp>HFNA@}iD z6y;jswLo|5Zc1T?Wihb~O+T~X`Vo5yYyA)Wd%vP#QTx<2#a|iiX>z9GuFOj>J=+zC zolH!%96n6SPz&w@S2D+njYeLSORf(%Fi49vSg6<>U^ZJ^ta#!ubKth`&Z4lef0?O1x$_ z+IR=P+f#x;@cCa~Jng2{bheM&$6%Gc=$`>U?UH*n1fOxPje$$=$?A7H$NZFH%e6C> zUX+L1xQ#xN9^kk{{?scWMG|U6lF;4Ln{bOC>orrEgHpV;(ztV%JmEgQByb+bHd1mG z3o?@9x3o^RF**fhpGBJ^Po&HBBJaB0)wDNu-@JIs_$U}$PX#hCYq0Gn*^p};l^Ipz zn@`SO>hWawbi$Uywi>-hE8zk>BSfuzD|ON@H+av|!a#(ZKy)v=rJ{$22ep>)*Xi^lvmTGV0XNUw*K zO=kO7U)%@JuFV;6VrP&;oA>Yg-Go|mgNsaGZJ__1KEmVOAEf#m9O9HJsF)Lr z$?Ez~q-{IB4p*c;sT`_#zg|b}p;)lW_2HA8V71g@Ophk3fj+kh5rtZnlK9kGrDkgC zF`Jw%Ak44#$0AH@v&{hjCq|h+D>EsdDN8-Qn_8TWA-lS>mVB;X{;2U4@tIE8y~?T% z+wsjTC&^U}#;d=ciRtOM>bqrReFspZO|hIgt{t_!bn{d}{Rc44@UqCE5#58Bqn9u_ zPBH3mZCOyFcIN5WXI`pSL0jU)BCRwEl{wV@n*i^>|CCEKoeY*99{*Rc ziRQ;bR+OHbX^-R8I59t-PE3TSu3|}Kn6VH}bX=<7^b61T{7~XSsVscD&lkvps2m!5 zylnzrDD>dUJa>5e9XBp#%D g*D-aQhD6-}7cC(sse* Date: Thu, 21 Nov 2024 16:15:21 -0500 Subject: [PATCH 115/175] Compute AmbientOcclusion step length from window and sample count --- .../AmbientOcclusionGenerate.glsl | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/packages/engine/Source/Shaders/PostProcessStages/AmbientOcclusionGenerate.glsl b/packages/engine/Source/Shaders/PostProcessStages/AmbientOcclusionGenerate.glsl index 74104993ec1d..946fdf885b8d 100644 --- a/packages/engine/Source/Shaders/PostProcessStages/AmbientOcclusionGenerate.glsl +++ b/packages/engine/Source/Shaders/PostProcessStages/AmbientOcclusionGenerate.glsl @@ -10,13 +10,13 @@ uniform float frustumLength; uniform int stepCount; uniform int directionCount; -vec3 pixelToEye(vec2 screenCoordinate) +vec4 pixelToEye(vec2 screenCoordinate) { vec2 uv = screenCoordinate / czm_viewport.zw; float depth = czm_readDepth(depthTexture, uv); vec2 xy = 2.0 * uv - vec2(1.0); vec4 posEC = czm_inverseProjection * vec4(xy, depth, 1.0); - return posEC.xyz / posEC.w; + return posEC / posEC.w; } // Reconstruct surface normal in eye coordinates, avoiding edges @@ -24,10 +24,10 @@ vec3 getNormalXEdge(vec3 positionEC) { // Find the 3D surface positions at adjacent screen pixels vec2 centerCoord = gl_FragCoord.xy; - vec3 positionLeft = pixelToEye(centerCoord + vec2(-1.0, 0.0)); - vec3 positionRight = pixelToEye(centerCoord + vec2(1.0, 0.0)); - vec3 positionUp = pixelToEye(centerCoord + vec2(0.0, 1.0)); - vec3 positionDown = pixelToEye(centerCoord + vec2(0.0, -1.0)); + vec3 positionLeft = pixelToEye(centerCoord + vec2(-1.0, 0.0)).xyz; + vec3 positionRight = pixelToEye(centerCoord + vec2(1.0, 0.0)).xyz; + vec3 positionUp = pixelToEye(centerCoord + vec2(0.0, 1.0)).xyz; + vec3 positionDown = pixelToEye(centerCoord + vec2(0.0, -1.0)).xyz; // Compute potential tangent vectors vec3 dx0 = positionEC - positionLeft; @@ -51,7 +51,7 @@ float gaussian(float x, float standardDeviation) { void main(void) { - vec3 positionEC = pixelToEye(gl_FragCoord.xy); + vec4 positionEC = pixelToEye(gl_FragCoord.xy); if (positionEC.z > frustumLength) { @@ -59,10 +59,11 @@ void main(void) return; } - vec3 normalEC = getNormalXEdge(positionEC); + vec3 normalEC = getNormalXEdge(positionEC.xyz); float gaussianVariance = lengthCap * sqrt(-positionEC.z); - // TODO: mix of units. Steps are in pixels; variance is in meters. - //float stepLength = max(1.0, 3.0 * gaussianVariance / (float(stepCount) + 1.0)); + // Choose a step length such that the marching stops just before 3 * variance. + float stepLength = 3.0 * gaussianVariance / (float(stepCount) + 1.0); + float pixelsPerStep = max(stepLength / czm_metersPerPixel(positionEC, 1.0), 1.0); float angleStepScale = 1.0 / float(directionCount); float angleStep = angleStepScale * czm_twoPi; @@ -92,8 +93,7 @@ void main(void) float localAO = 0.0; float accumulatedWindowWeights = 0.0; - //vec2 radialStep = stepLength * sampleDirection; - vec2 radialStep = stepSize * sampleDirection; + vec2 radialStep = pixelsPerStep * sampleDirection; #if __VERSION__ == 300 for (int j = 0; j < stepCount; j++) @@ -106,17 +106,17 @@ void main(void) } #endif // Step along sampling direction, away from output pixel - vec2 newCoords = floor(gl_FragCoord.xy + float(j + 1) * radialStep) + vec2(0.5); + vec2 samplePixel = floor(gl_FragCoord.xy + float(j + 1) * radialStep) + vec2(0.5); // Exit if we stepped off the screen - if (clamp(newCoords, vec2(0.0), czm_viewport.zw) != newCoords) + if (clamp(samplePixel, vec2(0.0), czm_viewport.zw) != samplePixel) { break; } // Compute step vector from output point to sampled point - vec3 stepPositionEC = pixelToEye(newCoords); - vec3 stepVector = stepPositionEC - positionEC; + vec4 samplePositionEC = pixelToEye(samplePixel); + vec3 stepVector = samplePositionEC.xyz - positionEC.xyz; // Estimate the angle from the surface normal. float dotVal = clamp(dot(normalEC, normalize(stepVector)), 0.0, 1.0); @@ -132,7 +132,7 @@ void main(void) // Compute lateral distance from output point, for weight normalization // TODO: This is slow! Better to analytically compute window scales - float lateralDistance = length(stepPositionEC.xy - positionEC.xy); + float lateralDistance = length(stepVector.xy); accumulatedWindowWeights += gaussian(lateralDistance, gaussianVariance); } ao += localAO / accumulatedWindowWeights; From 8a20ef041ac72e720a80fbfd59e611ce248ffa61 Mon Sep 17 00:00:00 2001 From: Mark Dane Date: Thu, 21 Nov 2024 16:59:58 -0500 Subject: [PATCH 116/175] Add one time warning and deprecation --- .../3D Tiles Vertical Exaggeration.html | 5 +- Apps/Sandcastle/gallery/AEC Clipping.html | 5 +- .../gallery/Clamp Entities to Ground.html | 5 +- Apps/Sandcastle/gallery/Clipping Regions.html | 5 +- ...alistic 3D Tiles with Building Insert.html | 5 +- .../Google Photorealistic 3D Tiles.html | 5 +- .../gallery/development/3D Tiles Picking.html | 5 +- CHANGES.md | 4 ++ .../createGooglePhotorealistic3DTileset.js | 63 ++++++++++++++----- 9 files changed, 81 insertions(+), 21 deletions(-) diff --git a/Apps/Sandcastle/gallery/3D Tiles Vertical Exaggeration.html b/Apps/Sandcastle/gallery/3D Tiles Vertical Exaggeration.html index 292e07945f11..41f67a60a1b6 100644 --- a/Apps/Sandcastle/gallery/3D Tiles Vertical Exaggeration.html +++ b/Apps/Sandcastle/gallery/3D Tiles Vertical Exaggeration.html @@ -105,7 +105,10 @@

Loading...

// Add Photorealistic 3D Tiles try { - const tileset = await Cesium.createGooglePhotorealistic3DTileset(); + const tileset = await Cesium.createGooglePhotorealistic3DTileset({ + // Only the Google Geocoder can be used with Google Photorealistic 3D Tiles. Set the `geocode` property of the viewer constructor options to IonGeocodeProviderType.GOOGLE. + onlyUsingWithGoogleGeocoder: true, + }); scene.primitives.add(tileset); } catch (error) { console.log(`Error loading Photorealistic 3D Tiles tileset. diff --git a/Apps/Sandcastle/gallery/AEC Clipping.html b/Apps/Sandcastle/gallery/AEC Clipping.html index 57c381068f9d..d373bd2a589a 100644 --- a/Apps/Sandcastle/gallery/AEC Clipping.html +++ b/Apps/Sandcastle/gallery/AEC Clipping.html @@ -49,7 +49,10 @@ // Add Photorealistic 3D Tiles let googleTileset; try { - googleTileset = await Cesium.createGooglePhotorealistic3DTileset(); + googleTileset = await Cesium.createGooglePhotorealistic3DTileset({ + // Only the Google Geocoder can be used with Google Photorealistic 3D Tiles. Set the `geocode` property of the viewer constructor options to IonGeocodeProviderType.GOOGLE. + onlyUsingWithGoogleGeocoder: true, + }); viewer.scene.primitives.add(googleTileset); } catch (error) { console.log(`Error loading Photorealistic 3D Tiles tileset. diff --git a/Apps/Sandcastle/gallery/Clamp Entities to Ground.html b/Apps/Sandcastle/gallery/Clamp Entities to Ground.html index 07b1f9afd3d9..e38e8709c464 100644 --- a/Apps/Sandcastle/gallery/Clamp Entities to Ground.html +++ b/Apps/Sandcastle/gallery/Clamp Entities to Ground.html @@ -57,7 +57,10 @@ let worldTileset; try { - worldTileset = await Cesium.createGooglePhotorealistic3DTileset(); + worldTileset = await Cesium.createGooglePhotorealistic3DTileset({ + // Only the Google Geocoder can be used with Google Photorealistic 3D Tiles. Set the `geocode` property of the viewer constructor options to IonGeocodeProviderType.GOOGLE. + onlyUsingWithGoogleGeocoder: true, + }); viewer.scene.primitives.add(worldTileset); } catch (error) { console.log(`Error loading Photorealistic 3D Tiles tileset. diff --git a/Apps/Sandcastle/gallery/Clipping Regions.html b/Apps/Sandcastle/gallery/Clipping Regions.html index 97dba26b3ce9..8f15df8f3c44 100644 --- a/Apps/Sandcastle/gallery/Clipping Regions.html +++ b/Apps/Sandcastle/gallery/Clipping Regions.html @@ -60,7 +60,10 @@ let worldTileset; try { - worldTileset = await Cesium.createGooglePhotorealistic3DTileset(); + worldTileset = await Cesium.createGooglePhotorealistic3DTileset({ + // Only the Google Geocoder can be used with Google Photorealistic 3D Tiles. Set the `geocode` property of the viewer constructor options to IonGeocodeProviderType.GOOGLE. + onlyUsingWithGoogleGeocoder: true, + }); scene.primitives.add(worldTileset); } catch (error) { console.log(`Error loading Photorealistic 3D Tiles tileset. diff --git a/Apps/Sandcastle/gallery/Google Photorealistic 3D Tiles with Building Insert.html b/Apps/Sandcastle/gallery/Google Photorealistic 3D Tiles with Building Insert.html index d93b4085a064..a75c993a8afa 100644 --- a/Apps/Sandcastle/gallery/Google Photorealistic 3D Tiles with Building Insert.html +++ b/Apps/Sandcastle/gallery/Google Photorealistic 3D Tiles with Building Insert.html @@ -43,7 +43,10 @@ // Add Photorealistic 3D Tiles try { - const googleTileset = await Cesium.createGooglePhotorealistic3DTileset(); + const googleTileset = await Cesium.createGooglePhotorealistic3DTileset({ + // Only the Google Geocoder can be used with Google Photorealistic 3D Tiles. Set the `geocode` property of the viewer constructor options to IonGeocodeProviderType.GOOGLE. + onlyUsingWithGoogleGeocoder: true, + }); viewer.scene.primitives.add(googleTileset); } catch (error) { console.log(`Error loading Photorealistic 3D Tiles tileset. diff --git a/Apps/Sandcastle/gallery/Google Photorealistic 3D Tiles.html b/Apps/Sandcastle/gallery/Google Photorealistic 3D Tiles.html index 59eec45e543f..2f9da653dc9c 100644 --- a/Apps/Sandcastle/gallery/Google Photorealistic 3D Tiles.html +++ b/Apps/Sandcastle/gallery/Google Photorealistic 3D Tiles.html @@ -43,7 +43,10 @@ // Add Photorealistic 3D Tiles try { - const tileset = await Cesium.createGooglePhotorealistic3DTileset(); + const tileset = await Cesium.createGooglePhotorealistic3DTileset({ + // Only the Google Geocoder can be used with Google Photorealistic 3D Tiles. Set the `geocode` property of the viewer constructor options to IonGeocodeProviderType.GOOGLE. + onlyUsingWithGoogleGeocoder: true, + }); viewer.scene.primitives.add(tileset); } catch (error) { console.log(`Error loading Photorealistic 3D Tiles tileset. diff --git a/Apps/Sandcastle/gallery/development/3D Tiles Picking.html b/Apps/Sandcastle/gallery/development/3D Tiles Picking.html index 10c3b2367a66..3cfdf92c9102 100644 --- a/Apps/Sandcastle/gallery/development/3D Tiles Picking.html +++ b/Apps/Sandcastle/gallery/development/3D Tiles Picking.html @@ -44,7 +44,10 @@ onselect: async () => { scene.primitives.remove(tileset); try { - tileset = await Cesium.createGooglePhotorealistic3DTileset(); + tileset = await Cesium.createGooglePhotorealistic3DTileset({ + // Only the Google Geocoder can be used with Google Photorealistic 3D Tiles. Set the `geocode` property of the viewer constructor options to IonGeocodeProviderType.GOOGLE. + onlyUsingWithGoogleGeocoder: true, + }); scene.primitives.add(tileset); } catch (error) { console.log(error); diff --git a/CHANGES.md b/CHANGES.md index 454c7f67a5f0..4c9228255cea 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -14,6 +14,10 @@ - Fix label rendering bug in WebGL1 contexts. [#12301](https://github.com/CesiumGS/cesium/pull/12301) - Added `GoogleGeocoderService` for standalone usage of Google geocoder. [#12299](https://github.com/CesiumGS/cesium/pull/12299) +##### Deprecated :hourglass_flowing_sand: + +- `createGooglePhotorealistic3DTileset(key)` has been deprecated. Use `createGooglePhotorealistic3DTileset({key})` instead. It will be removed in 1.126. + #### @cesium/widgets ##### Additions :tada: diff --git a/packages/engine/Source/Scene/createGooglePhotorealistic3DTileset.js b/packages/engine/Source/Scene/createGooglePhotorealistic3DTileset.js index 8378df0050d5..9c7bad99e30d 100644 --- a/packages/engine/Source/Scene/createGooglePhotorealistic3DTileset.js +++ b/packages/engine/Source/Scene/createGooglePhotorealistic3DTileset.js @@ -4,14 +4,24 @@ import defined from "../Core/defined.js"; import IonResource from "../Core/IonResource.js"; import GoogleMaps from "../Core/GoogleMaps.js"; import Resource from "../Core/Resource.js"; +import oneTimeWarning from "../Core/oneTimeWarning.js"; +import deprecationWarning from "../Core/deprecationWarning.js"; /** - * Creates a {@link Cesium3DTileset} instance for the Google Photorealistic 3D Tiles tileset. + * Creates a {@link Cesium3DTileset} instance for the Google Photorealistic 3D + * Tiles tileset. + * + * Google Photorealistic 3D Tiles can only be used with the Google geocoder. To + * confirm that you are aware of this restriction pass + * `usingOnlyWithGoogleGeocoder: true` to the apiOptions. Otherwise a one time + * warning will be displayed when this function is called. * * @function * - * @param {string} [key=GoogleMaps.defaultApiKey] Your API key to access Google Photorealistic 3D Tiles. See {@link https://developers.google.com/maps/documentation/javascript/get-api-key} for instructions on how to create your own key. - * @param {Cesium3DTileset.ConstructorOptions} [options] An object describing initialization options. + * @param {object} [apiOptions] + * @param {string} [apiOptions.key=GoogleMaps.defaultApiKey] Your API key to access Google Photorealistic 3D Tiles. See {@link https://developers.google.com/maps/documentation/javascript/get-api-key} for instructions on how to create your own key. + * @param {true} [apiOptions.onlyUsingWithGoogleGeocoder] Confirmation that this tileset will only be used with the Google geocoder. + * @param {Cesium3DTileset.ConstructorOptions} [tilesetOptions] An object describing initialization options. * @returns {Promise} * * @see GoogleMaps @@ -22,7 +32,9 @@ import Resource from "../Core/Resource.js"; * }); * * try { - * const tileset = await Cesium.createGooglePhotorealistic3DTileset(); + * const tileset = await Cesium.createGooglePhotorealistic3DTileset({ + * onlyUsingWithGoogleGeocoder: true, + * }); * viewer.scene.primitives.add(tileset)); * } catch (error) { * console.log(`Error creating tileset: ${error}`); @@ -37,24 +49,47 @@ import Resource from "../Core/Resource.js"; * }); * * try { - * const tileset = await Cesium.createGooglePhotorealistic3DTileset(); + * const tileset = await Cesium.createGooglePhotorealistic3DTileset({ + * onlyUsingWithGoogleGeocoder: true, + * }); * viewer.scene.primitives.add(tileset)); * } catch (error) { * console.log(`Error creating tileset: ${error}`); * } */ -async function createGooglePhotorealistic3DTileset(key, options) { - options = defaultValue(options, {}); - options.cacheBytes = defaultValue(options.cacheBytes, 1536 * 1024 * 1024); - options.maximumCacheOverflowBytes = defaultValue( - options.maximumCacheOverflowBytes, +async function createGooglePhotorealistic3DTileset(apiOptions, tilesetOptions) { + tilesetOptions = defaultValue(tilesetOptions, {}); + tilesetOptions.cacheBytes = defaultValue( + tilesetOptions.cacheBytes, + 1536 * 1024 * 1024, + ); + tilesetOptions.maximumCacheOverflowBytes = defaultValue( + tilesetOptions.maximumCacheOverflowBytes, 1024 * 1024 * 1024, ); - options.enableCollision = defaultValue(options.enableCollision, true); + tilesetOptions.enableCollision = defaultValue( + tilesetOptions.enableCollision, + true, + ); - key = defaultValue(key, GoogleMaps.defaultApiKey); + apiOptions = defaultValue(apiOptions, defaultValue.EMPTY_OBJECT); + if (typeof apiOptions === "string") { + deprecationWarning( + "createGooglePhotorealistic3DTileset(key)", + "createGooglePhotorealistic3DTileset(key) has been deprecated. It is replaced by createGooglePhotorealistic3DTileset({key}). It will be removed in Cesium 1.126.", + ); + apiOptions = { key: apiOptions }; + } + if (!apiOptions.onlyUsingWithGoogleGeocoder) { + oneTimeWarning( + "google-tiles-with-google-geocoder", + "Only the Google geocoder can be used with Google Photorealistic 3D Tiles. Set the `geocode` property of Viewer constructor options. You can set additionalOptions.onlyUsingWithGoogleGeocoder to hide this warning once you have configured the geocoder.", + ); + } + + const key = defaultValue(apiOptions.key, GoogleMaps.defaultApiKey); if (!defined(key)) { - return requestCachedIonTileset(options); + return requestCachedIonTileset(tilesetOptions); } let credits; @@ -71,7 +106,7 @@ async function createGooglePhotorealistic3DTileset(key, options) { credits: credits, }); - return Cesium3DTileset.fromUrl(resource, options); + return Cesium3DTileset.fromUrl(resource, tilesetOptions); } const metadataCache = {}; From 8da2b0632dff1129b9edc980b9afd180ccc39a08 Mon Sep 17 00:00:00 2001 From: Jeshurun Hembd Date: Thu, 21 Nov 2024 19:17:22 -0500 Subject: [PATCH 117/175] Compute Ambient Occlusion weight normalization analytically --- .../Source/Scene/PostProcessStageLibrary.js | 1 - .../AmbientOcclusionGenerate.glsl | 20 ++++++++----------- 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/packages/engine/Source/Scene/PostProcessStageLibrary.js b/packages/engine/Source/Scene/PostProcessStageLibrary.js index 1f21f1ab2961..6e68acc2a648 100644 --- a/packages/engine/Source/Scene/PostProcessStageLibrary.js +++ b/packages/engine/Source/Scene/PostProcessStageLibrary.js @@ -511,7 +511,6 @@ PostProcessStageLibrary.createAmbientOcclusionStage = function () { lengthCap: 0.26, directionCount: 16, stepCount: 64, - stepSize: 1.95, frustumLength: 1000.0, randomTexture: undefined, }, diff --git a/packages/engine/Source/Shaders/PostProcessStages/AmbientOcclusionGenerate.glsl b/packages/engine/Source/Shaders/PostProcessStages/AmbientOcclusionGenerate.glsl index 946fdf885b8d..00d4e141ee01 100644 --- a/packages/engine/Source/Shaders/PostProcessStages/AmbientOcclusionGenerate.glsl +++ b/packages/engine/Source/Shaders/PostProcessStages/AmbientOcclusionGenerate.glsl @@ -5,7 +5,6 @@ uniform sampler2D depthTexture; uniform float intensity; uniform float bias; uniform float lengthCap; -uniform float stepSize; uniform float frustumLength; uniform int stepCount; uniform int directionCount; @@ -63,7 +62,10 @@ void main(void) float gaussianVariance = lengthCap * sqrt(-positionEC.z); // Choose a step length such that the marching stops just before 3 * variance. float stepLength = 3.0 * gaussianVariance / (float(stepCount) + 1.0); - float pixelsPerStep = max(stepLength / czm_metersPerPixel(positionEC, 1.0), 1.0); + float metersPerPixel = czm_metersPerPixel(positionEC, 1.0); + // Minimum step is 1 pixel to avoid double sampling + float pixelsPerStep = max(stepLength / metersPerPixel, 1.0); + stepLength = pixelsPerStep * metersPerPixel; float angleStepScale = 1.0 / float(directionCount); float angleStep = angleStepScale * czm_twoPi; @@ -83,7 +85,7 @@ void main(void) for (int i = 0; i < directionCount; i++) { #else - for (int i = 0; i < 64; i++) + for (int i = 0; i < 16; i++) { if (i >= directionCount) { break; @@ -92,14 +94,13 @@ void main(void) sampleDirection = rotateStep * sampleDirection; float localAO = 0.0; - float accumulatedWindowWeights = 0.0; vec2 radialStep = pixelsPerStep * sampleDirection; #if __VERSION__ == 300 for (int j = 0; j < stepCount; j++) { #else - for (int j = 0; j < 128; j++) + for (int j = 0; j < 64; j++) { if (j >= stepCount) { break; @@ -129,16 +130,11 @@ void main(void) float sampleDistance = length(stepVector); float weight = gaussian(sampleDistance, gaussianVariance); localAO += weight * dotVal; - - // Compute lateral distance from output point, for weight normalization - // TODO: This is slow! Better to analytically compute window scales - float lateralDistance = length(stepVector.xy); - accumulatedWindowWeights += gaussian(lateralDistance, gaussianVariance); } - ao += localAO / accumulatedWindowWeights; + ao += localAO; } - ao *= angleStepScale; + ao *= angleStepScale * stepLength; ao = 1.0 - clamp(ao, 0.0, 1.0); ao = pow(ao, intensity); out_FragColor = vec4(vec3(ao), 1.0); From 58d7e08d21de804b1d3f6f995aa323b6486e0abf Mon Sep 17 00:00:00 2001 From: Jeshurun Hembd Date: Thu, 21 Nov 2024 19:24:14 -0500 Subject: [PATCH 118/175] Fix spec for ambient occlusion uniforms --- packages/engine/Specs/Scene/PostProcessStageLibrarySpec.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/engine/Specs/Scene/PostProcessStageLibrarySpec.js b/packages/engine/Specs/Scene/PostProcessStageLibrarySpec.js index 3350a5a2eb54..36cb43bac97f 100644 --- a/packages/engine/Specs/Scene/PostProcessStageLibrarySpec.js +++ b/packages/engine/Specs/Scene/PostProcessStageLibrarySpec.js @@ -392,7 +392,8 @@ describe( expect(ao.uniforms.intensity).toEqual(3.0); expect(ao.uniforms.bias).toEqual(0.1); expect(ao.uniforms.lengthCap).toEqual(0.26); - expect(ao.uniforms.stepSize).toEqual(1.95); + expect(ao.uniforms.directionCount).toEqual(16); + expect(ao.uniforms.stepCount).toEqual(64); expect(ao.uniforms.frustumLength).toEqual(1000.0); expect(ao.uniforms.randomTexture).not.toBeDefined(); expect(ao.uniforms.delta).toEqual(1.0); From 1f823a81347c64e2af946de7940bb1f21c10d105 Mon Sep 17 00:00:00 2001 From: Ryan Veenstra <123468416+r-veenstra@users.noreply.github.com> Date: Fri, 22 Nov 2024 20:48:58 +1000 Subject: [PATCH 119/175] Add new AEC design sandcastle examples --- .../gallery/AEC Architectural Design.html | 231 ++++++++++++++++++ .../gallery/AEC Architectural Design.jpg | Bin 0 -> 19333 bytes .../gallery/AEC Isolate by Category.html | 151 ++++++++++++ .../gallery/AEC Isolate by Category.jpg | Bin 0 -> 20154 bytes .../gallery/AEC Metadata Styling.html | 187 ++++++++++++++ .../gallery/AEC Metadata Styling.jpg | Bin 0 -> 20721 bytes CONTRIBUTORS.md | 1 + 7 files changed, 570 insertions(+) create mode 100644 Apps/Sandcastle/gallery/AEC Architectural Design.html create mode 100644 Apps/Sandcastle/gallery/AEC Architectural Design.jpg create mode 100644 Apps/Sandcastle/gallery/AEC Isolate by Category.html create mode 100644 Apps/Sandcastle/gallery/AEC Isolate by Category.jpg create mode 100644 Apps/Sandcastle/gallery/AEC Metadata Styling.html create mode 100644 Apps/Sandcastle/gallery/AEC Metadata Styling.jpg diff --git a/Apps/Sandcastle/gallery/AEC Architectural Design.html b/Apps/Sandcastle/gallery/AEC Architectural Design.html new file mode 100644 index 000000000000..e7a1a7244238 --- /dev/null +++ b/Apps/Sandcastle/gallery/AEC Architectural Design.html @@ -0,0 +1,231 @@ + + + + + + + + + + Cesium Demo + + + + + + +
+

Loading...

+
+ + + diff --git a/Apps/Sandcastle/gallery/AEC Architectural Design.jpg b/Apps/Sandcastle/gallery/AEC Architectural Design.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e6753bb4f5c9f3c0a9f68a531c01359a2fc20029 GIT binary patch literal 19333 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 MueJR9zUP(y3l`P7!2kdN literal 0 HcmV?d00001 diff --git a/Apps/Sandcastle/gallery/AEC Isolate by Category.html b/Apps/Sandcastle/gallery/AEC Isolate by Category.html new file mode 100644 index 000000000000..84931a5ada99 --- /dev/null +++ b/Apps/Sandcastle/gallery/AEC Isolate by Category.html @@ -0,0 +1,151 @@ + + + + + + + + + + Cesium Demo + + + + + + +
+

Loading...

+
+ + + diff --git a/Apps/Sandcastle/gallery/AEC Isolate by Category.jpg b/Apps/Sandcastle/gallery/AEC Isolate by Category.jpg new file mode 100644 index 0000000000000000000000000000000000000000..81f2db7cedf78cf52f5eee0a1eaa19934b2d9812 GIT binary patch literal 20154 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 literal 0 HcmV?d00001 diff --git a/Apps/Sandcastle/gallery/AEC Metadata Styling.html b/Apps/Sandcastle/gallery/AEC Metadata Styling.html new file mode 100644 index 000000000000..be01bff64879 --- /dev/null +++ b/Apps/Sandcastle/gallery/AEC Metadata Styling.html @@ -0,0 +1,187 @@ + + + + + + + + + + Cesium Demo + + + + + + +
+

Loading...

+
+
+ + +
+
+ + +
+
+ + + diff --git a/Apps/Sandcastle/gallery/AEC Metadata Styling.jpg b/Apps/Sandcastle/gallery/AEC Metadata Styling.jpg new file mode 100644 index 0000000000000000000000000000000000000000..4fa9750411e0df7d12b318112f32c21033ad9fd6 GIT binary patch literal 20721 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
FUIndQO3m>1;=YIrhrjj8{k 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 E*);Mt6aWAK literal 0 HcmV?d00001 diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 46b59573acfd..bc8598aeda78 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -168,6 +168,7 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for details on how to contribute to Cesiu - [Siddhesh Ranade](https://github.com/siddheshranade) - [Adam Morris](https://github.com/weegeekps) - [Luke McKinstry](https://github.com/lukemckinstry) + - [Ryan Veenstra](https://github.com/r-veenstra) - [Northrop Grumman](http://www.northropgrumman.com) - [Joseph Stein](https://github.com/nahgrin) - [EOX IT Services GmbH](https://eox.at) From 54f3e1c3366c312add9599061ba694c18b00738d Mon Sep 17 00:00:00 2001 From: Ryan Veenstra <123468416+r-veenstra@users.noreply.github.com> Date: Fri, 22 Nov 2024 21:27:49 +1000 Subject: [PATCH 120/175] Update descriptions --- Apps/Sandcastle/gallery/AEC Architectural Design.html | 5 ++++- Apps/Sandcastle/gallery/AEC Isolate by Category.html | 5 ++++- Apps/Sandcastle/gallery/AEC Metadata Styling.html | 5 ++++- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/Apps/Sandcastle/gallery/AEC Architectural Design.html b/Apps/Sandcastle/gallery/AEC Architectural Design.html index e7a1a7244238..5b4167fd5187 100644 --- a/Apps/Sandcastle/gallery/AEC Architectural Design.html +++ b/Apps/Sandcastle/gallery/AEC Architectural Design.html @@ -8,7 +8,10 @@ name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Cesium Demo diff --git a/Apps/Sandcastle/gallery/AEC Isolate by Category.html b/Apps/Sandcastle/gallery/AEC Isolate by Category.html index 84931a5ada99..7e5aae45762a 100644 --- a/Apps/Sandcastle/gallery/AEC Isolate by Category.html +++ b/Apps/Sandcastle/gallery/AEC Isolate by Category.html @@ -8,7 +8,10 @@ name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Cesium Demo diff --git a/Apps/Sandcastle/gallery/AEC Metadata Styling.html b/Apps/Sandcastle/gallery/AEC Metadata Styling.html index be01bff64879..e46fe196272b 100644 --- a/Apps/Sandcastle/gallery/AEC Metadata Styling.html +++ b/Apps/Sandcastle/gallery/AEC Metadata Styling.html @@ -8,7 +8,10 @@ name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> - + Cesium Demo From 154c7051dca5088e711123bdf08308899bd5d8fe Mon Sep 17 00:00:00 2001 From: Mark Dane Date: Fri, 22 Nov 2024 10:11:12 -0500 Subject: [PATCH 121/175] Minor PR feedback --- Apps/Sandcastle/gallery/Clipping Regions.html | 1 + Apps/Sandcastle/gallery/development/3D Tiles Picking.html | 1 + packages/engine/Source/Core/IonGeocoderService.js | 2 ++ 3 files changed, 4 insertions(+) diff --git a/Apps/Sandcastle/gallery/Clipping Regions.html b/Apps/Sandcastle/gallery/Clipping Regions.html index 8f15df8f3c44..b7116fc29f17 100644 --- a/Apps/Sandcastle/gallery/Clipping Regions.html +++ b/Apps/Sandcastle/gallery/Clipping Regions.html @@ -43,6 +43,7 @@ animation: false, sceneModePicker: false, baseLayerPicker: false, + geocoder: Cesium.IonGeocoderProviderType.GOOGLE, }); const scene = viewer.scene; diff --git a/Apps/Sandcastle/gallery/development/3D Tiles Picking.html b/Apps/Sandcastle/gallery/development/3D Tiles Picking.html index 3cfdf92c9102..ce77a1cc5f35 100644 --- a/Apps/Sandcastle/gallery/development/3D Tiles Picking.html +++ b/Apps/Sandcastle/gallery/development/3D Tiles Picking.html @@ -34,6 +34,7 @@ animation: false, baseLayerPicker: false, globe: false, + geocoder: false, }); const scene = viewer.scene; diff --git a/packages/engine/Source/Core/IonGeocoderService.js b/packages/engine/Source/Core/IonGeocoderService.js index c91785229f04..f473d80005b3 100644 --- a/packages/engine/Source/Core/IonGeocoderService.js +++ b/packages/engine/Source/Core/IonGeocoderService.js @@ -65,7 +65,9 @@ function IonGeocoderService(options) { options.geocodeProviderType, IonGeocodeProviderType.DEFAULT, ); + //>>includeStart('debug', pragmas.debug); validateIonGeocodeProviderType(geocodeProviderType); + //>>includeEnd('debug'); const accessToken = defaultValue(options.accessToken, Ion.defaultAccessToken); const server = Resource.createIfNeeded( From 0c5826ceaa36e57cb7999a73452521fe936ce872 Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Fri, 22 Nov 2024 19:02:52 +0100 Subject: [PATCH 122/175] Update defined check Co-authored-by: Gabby Getz --- Apps/Sandcastle/gallery/3D Tiles NGA GPM Visualization.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Apps/Sandcastle/gallery/3D Tiles NGA GPM Visualization.html b/Apps/Sandcastle/gallery/3D Tiles NGA GPM Visualization.html index 9c79f18dcdb0..c5b47c2960ca 100644 --- a/Apps/Sandcastle/gallery/3D Tiles NGA GPM Visualization.html +++ b/Apps/Sandcastle/gallery/3D Tiles NGA GPM Visualization.html @@ -1251,10 +1251,10 @@

Cesium GPM Visualization

* @param {Cesium3DTileset|undefined} newCurrentTileset The new tileset */ updateForCurrentTilesetChanged(oldCurrentTileset, newCurrentTileset) { - if (oldCurrentTileset !== undefined) { + if (Cesium.defined(oldCurrentTileset)) { oldCurrentTileset.tileVisible.removeEventListener(this.tileVisibleListener); } - if (newCurrentTileset !== undefined) { + if (Cesium.defined(newCurrentTileset)) { newCurrentTileset.tileVisible.addEventListener(this.tileVisibleListener); } } From 9a641a5a2225a8856ee4eefad111bf3c61029e1e Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Fri, 22 Nov 2024 19:03:16 +0100 Subject: [PATCH 123/175] Update defined check Co-authored-by: Gabby Getz --- Apps/Sandcastle/gallery/3D Tiles NGA GPM Visualization.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Apps/Sandcastle/gallery/3D Tiles NGA GPM Visualization.html b/Apps/Sandcastle/gallery/3D Tiles NGA GPM Visualization.html index c5b47c2960ca..a102825ca013 100644 --- a/Apps/Sandcastle/gallery/3D Tiles NGA GPM Visualization.html +++ b/Apps/Sandcastle/gallery/3D Tiles NGA GPM Visualization.html @@ -1232,12 +1232,12 @@

Cesium GPM Visualization

const content = tile?.content; const modelContent = content instanceof Cesium.Model3DTileContent ? content : undefined; - if (!modelContent) { + if (!Cesium.defined(modelContent)) { return undefined; } const extensionObject = modelContent.getExtension("NGA_gpm_local"); - if (!extensionObject) { + if (!Cesium.defined(extensionObject)) { return undefined; } return extensionObject.anchorPointsIndirect; From 708a83a1025d3905a3e6d7c2ea0ee0c5a7cb1f2d Mon Sep 17 00:00:00 2001 From: Marco Hutter Date: Fri, 22 Nov 2024 19:05:21 +0100 Subject: [PATCH 124/175] Apply suggestions from code review Mainly defined checks Co-authored-by: Gabby Getz --- .../gallery/3D Tiles NGA GPM Visualization.html | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/Apps/Sandcastle/gallery/3D Tiles NGA GPM Visualization.html b/Apps/Sandcastle/gallery/3D Tiles NGA GPM Visualization.html index a102825ca013..40f008469ef3 100644 --- a/Apps/Sandcastle/gallery/3D Tiles NGA GPM Visualization.html +++ b/Apps/Sandcastle/gallery/3D Tiles NGA GPM Visualization.html @@ -285,7 +285,7 @@

Cesium GPM Visualization

* @private */ updateCustomShaderInTileset() { - if (this.currentTileset === undefined) { + if (!Cesium.defined(this.currentTileset)) { return; } this.currentTileset.customShader = this.currentCustomShader; @@ -307,7 +307,7 @@

Cesium GPM Visualization

* @private */ updateTextureThresholdInShader() { - if (this.currentCustomShader === undefined) { + if (!Cesium.defined(this.currentCustomShader)) { return; } const hasTextureThreshold = Object.keys( @@ -347,7 +347,7 @@

Cesium GPM Visualization

* Zoom to the current tileset, with a small, unspecified offset... */ zoomToCurrentTileset() { - if (this.currentTileset === undefined) { + if (!Cesium.defined(this.currentTileset)) { return; } const offset = new Cesium.HeadingPitchRange( @@ -407,10 +407,10 @@

Cesium GPM Visualization

propertyNameY, ); - if (!metadataValueX) { + if (!Cesium.defined(metadataValueX)) { metadataValueX = 0; } - if (!metadataValueY) { + if (!Cesium.defined(metadataValueY)) { metadataValueY = 0; } result.x = metadataValueX; @@ -678,7 +678,6 @@

Cesium GPM Visualization

* properties. * * @param {string} title The title to be displayed in the combo box - * @param {string} title The title to be displayed in the combo box * @param {string|undefined} propertyName0 The property name 0 * @param {number} sourceMin0 The minimum source value 0 * @param {number} sourceMax0 The maximum source value 0 @@ -840,7 +839,7 @@

Cesium GPM Visualization

return; } const worldPosition = viewer.scene.pickPosition(movement.position); - if (worldPosition === undefined) { + if (!Cesium.defined(worldPosition)) { that.clear(); } else { const uncertainty = pickUncertaintyFromTexture(movement.position); From d2055b277c72665b001fd08003d03913d0bd5b75 Mon Sep 17 00:00:00 2001 From: jjspace <8007967+jjspace@users.noreply.github.com> Date: Fri, 22 Nov 2024 13:53:23 -0500 Subject: [PATCH 125/175] pull out dev auth server --- .prettierignore | 1 - itwin-oauth-demo/.gitignore | 1 - itwin-oauth-demo/index.html | 34 ------------------- itwin-oauth-demo/server.js | 66 ------------------------------------- 4 files changed, 102 deletions(-) delete mode 100644 itwin-oauth-demo/.gitignore delete mode 100644 itwin-oauth-demo/index.html delete mode 100644 itwin-oauth-demo/server.js diff --git a/.prettierignore b/.prettierignore index e0d51a3709d0..c46eee96cb50 100644 --- a/.prettierignore +++ b/.prettierignore @@ -11,7 +11,6 @@ !packages/**/ !Specs/**/ !Tools/**/ -!itwin-oauth-demo/**/ !**/*.js !**/*.cjs diff --git a/itwin-oauth-demo/.gitignore b/itwin-oauth-demo/.gitignore deleted file mode 100644 index d344ba6b06cb..000000000000 --- a/itwin-oauth-demo/.gitignore +++ /dev/null @@ -1 +0,0 @@ -config.json diff --git a/itwin-oauth-demo/index.html b/itwin-oauth-demo/index.html deleted file mode 100644 index af6bc98d6499..000000000000 --- a/itwin-oauth-demo/index.html +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - Auth! - - - - - diff --git a/itwin-oauth-demo/server.js b/itwin-oauth-demo/server.js deleted file mode 100644 index 8a935343622e..000000000000 --- a/itwin-oauth-demo/server.js +++ /dev/null @@ -1,66 +0,0 @@ -import express from "express"; -import { readFileSync, writeFileSync } from "fs"; -import { dirname, join } from "path"; -import { exit } from "process"; -import { fileURLToPath } from "url"; - -let config = { - serviceapp: { - clientId: "", - clientSecret: "", - }, - port: 3000, -}; - -const __dirname = dirname(fileURLToPath(import.meta.url)); -const configPath = join(__dirname, "./config.json"); -try { - const configFile = readFileSync(configPath, { encoding: "utf-8" }); - config = JSON.parse(configFile); -} catch { - console.log("config file missing, default written to", configPath); - console.log("Please update the config with the desired values"); - writeFileSync(configPath, JSON.stringify(config, undefined, 2)); - exit(1); -} - -const app = express(); -const port = config.port ?? 3000; - -// eslint-disable-next-line no-unused-vars -app.get("/service", async (req, res) => { - console.log("/service request received"); - - const body = new URLSearchParams(); - body.set("grant_type", "client_credentials"); - body.set("client_id", config.serviceapp.clientId); - body.set("client_secret", config.serviceapp.clientSecret); - body.set("scope", "itwin-platform"); - - const response = await fetch("https://ims.bentley.com/connect/token", { - method: "POST", - body, - }); - - const result = await response.json(); - - res.setHeader("Access-Control-Allow-Origin", "*"); - - if (!response.ok || !result) { - console.log(" bad response/no result"); - res.status(response.status).send(); - return; - } - const { access_token } = result; - if (access_token) { - console.log(" token acquired, returned"); - res.status(200).send({ token: access_token }); - return; - } - console.log(" token not found"); - res.status(404).send("token not found"); -}); - -app.listen(port, () => { - console.log(`Server listening on port ${port}`); -}); From 47e1642eabffb26f301661f1ea1815a4f90bb38e Mon Sep 17 00:00:00 2001 From: jjspace <8007967+jjspace@users.noreply.github.com> Date: Fri, 22 Nov 2024 15:17:31 -0500 Subject: [PATCH 126/175] condense code, update docs --- CHANGES.md | 1 + packages/engine/Source/Core/ITwinPlatform.js | 23 +++--- packages/engine/Source/Scene/ITwinData.js | 80 +++++++++----------- 3 files changed, 50 insertions(+), 54 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 49e2c0056ac6..f6b0782a351a 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -7,6 +7,7 @@ ##### Additions :tada: - Added `Entity.trackingReferenceFrame` property to allow tracking entities in their own inertial reference frame. [#12194](https://github.com/CesiumGS/cesium/pull/12194) +- Added a new integration with the iTwin Platform to easily load iModels directly in the viewer. Use `ITwinPlatform.defaultAccessToken` to set the access token then use `ITwinData.createTilesetFromIModelId(iModelId)` to load the iModel as a `Cesium3DTileset`. [#12289](https://github.com/CesiumGS/cesium/pull/12289) ##### Fixes :wrench: diff --git a/packages/engine/Source/Core/ITwinPlatform.js b/packages/engine/Source/Core/ITwinPlatform.js index cbb8499aac0a..a21a2f6e0276 100644 --- a/packages/engine/Source/Core/ITwinPlatform.js +++ b/packages/engine/Source/Core/ITwinPlatform.js @@ -58,6 +58,7 @@ ITwinPlatform.apiEndpoint = new Resource({ /** * @typedef {Object} ExportRequest + * @private * @property {string} iModelId * @property {string} changesetId * @property {ITwinPlatform.ExportType} exportType Type of the export. CesiumJS only supports the 3DTILES type @@ -65,12 +66,14 @@ ITwinPlatform.apiEndpoint = new Resource({ /** * @typedef {Object} Link + * @private * @property {string} href */ /** * @typedef {Object} ExportRepresentation * The export objects from get-exports when using return=representation + * @private * @property {string} id Export id * @property {string} displayName Name of the iModel * @property {ITwinPlatform.ExportStatus} status Status of this export @@ -81,24 +84,21 @@ ITwinPlatform.apiEndpoint = new Resource({ /** * @typedef {Object} GetExportsResponse + * @private * @property {ExportRepresentation[]} exports The list of exports for the current page * @property {{self: Link, next: Link | undefined, prev: Link | undefined}} _links Pagination links */ /** - * Get the list of exports for the specified iModel at it's most current version. This will only return exports with {@link ITwinPlatform.ExportType} of 3DTILES. + * Get the list of exports for the specified iModel at it's most current version. + * This will only return the top 5 exports with {@link ITwinPlatform.ExportType} of 3DTILES. * - * @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy. * @private * * @param {string} iModelId iModel id - * - * @throws {RuntimeError} Unauthorized, bad token, wrong scopes or headers bad. - * @throws {RuntimeError} Not allowed, forbidden - * @throws {RuntimeError} Unprocessable Entity - * @throws {RuntimeError} Too many requests - * @throws {RuntimeError} Unknown request failure * @returns {Promise} + * + * @throws {RuntimeError} If the iTwin API request is not successful */ ITwinPlatform.getExports = async function (iModelId) { //>>includeStart('debug', pragmas.debug); @@ -118,9 +118,10 @@ ITwinPlatform.getExports = async function (iModelId) { queryParameters: { iModelId: iModelId, exportType: ITwinPlatform.ExportType["3DTILES"], - // TODO: If we're only requesting the top 1 is there a chance it's `Invalid` instead of `Complete` - // and never possible to load it? - $top: "1", + // With the export auto-generation it will auto-delete the 6th export so + // there should never be more than 5 results. Just request them all and parse + // for ones that are COMPLETE + $top: "5", client: "CesiumJS", }, }); diff --git a/packages/engine/Source/Scene/ITwinData.js b/packages/engine/Source/Scene/ITwinData.js index 70de32fe5e9f..219480c52ba2 100644 --- a/packages/engine/Source/Scene/ITwinData.js +++ b/packages/engine/Source/Scene/ITwinData.js @@ -3,7 +3,6 @@ import defined from "../Core/defined.js"; import Resource from "../Core/Resource.js"; import ITwinPlatform from "../Core/ITwinPlatform.js"; import RuntimeError from "../Core/RuntimeError.js"; -import Check from "../Core/Check.js"; /** * Methods for loading iTwin platform data into CesiumJS @@ -16,27 +15,51 @@ import Check from "../Core/Check.js"; const ITwinData = {}; /** - * @param {ExportRepresentation} exportObj - * @param {Cesium3DTileset.ConstructorOptions} [options] Object containing options to pass to an internally created {@link Cesium3DTileset}. - * @returns {Promise} + * Create a {@link Cesium3DTileset} for the given iModel id using the mesh export service. + * + * If there is not a completed export available for the given iModel id this function will return undefined. + * We recommend waiting 10-20 seconds and trying to load the tileset again. + * If all exports are Invalid this will throw an error. In that case there's likely something wrong with the iModel itself + * + * @example + * const tileset = await Cesium.ITwinData.createTilesetFromIModelId(iModelId); + * if (Cesium.defined(tileset)) { + * viewer.scene.primitives.add(tileset); + * } + * + * @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy. + * + * @param {string} iModelId The id of the iModel to load + * @param {Cesium3DTileset.ConstructorOptions} [options] Object containing options to pass to the internally created {@link Cesium3DTileset}. + * @returns {Promise} Will return undefined if there is no completed export for the given iModel id + * + * @throws {RuntimeError} If all exports for the given iModel are Invalid + * @throws {RuntimeError} If the iTwin API request is not successful */ -async function loadExport(exportObj, options) { - //>>includeStart('debug', pragmas.debug); - Check.defined("exportObj", exportObj); - //>>includeEnd('debug') +ITwinData.createTilesetFromIModelId = async function (iModelId, options) { + const { exports } = await ITwinPlatform.getExports(iModelId); - if (exportObj.request.exportType !== ITwinPlatform.ExportType["3DTILES"]) { - throw new RuntimeError(`Wrong export type ${exportObj.request.exportType}`); - } - if (exportObj.status !== ITwinPlatform.ExportStatus.Complete) { + if ( + exports.every((exportObj) => { + return exportObj.status === ITwinPlatform.ExportStatus.Invalid; + }) + ) { throw new RuntimeError( - `Export is not completed. ${exportObj.id} - ${exportObj.status}`, + `All exports for this iModel are Invalid: ${iModelId}`, ); } + const completeExport = exports.find((exportObj) => { + return exportObj.status === ITwinPlatform.ExportStatus.Complete; + }); + + if (!defined(completeExport)) { + return; + } + // Convert the link to the tileset url while preserving the search paramaters // This link is only valid 1 hour - const baseUrl = new URL(exportObj._links.mesh.href); + const baseUrl = new URL(completeExport._links.mesh.href); baseUrl.pathname = `${baseUrl.pathname}/tileset.json`; const tilesetUrl = baseUrl.toString(); @@ -45,35 +68,6 @@ async function loadExport(exportObj, options) { }); return Cesium3DTileset.fromUrl(resource, options); -} - -/** - * Loads the export for the specified iModel with the export type that CesiumJS can load and returns - * a tileset created from that export. - * - * If the export is not finished processing this will throw an error. It is up to the caller - * to re-attempt loading at a later time - * - * @example - * const tileset = await Cesium.ITwinData.createTilesetFromIModelId(iModelId); - * viewer.scene.primitives.add(tileset); - * - * @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy. - * - * @param {string} iModelId The id of the iModel to load - * @param {Cesium3DTileset.ConstructorOptions} [options] Object containing options to pass to an internally created {@link Cesium3DTileset}. - * @returns {Promise} Will return undefined if there is no export for the given iModel id - * - * @throws {RuntimeError} Wrong export type [type] - * @throws {RuntimeError} Export is not completed. [id] - [status] - */ -ITwinData.createTilesetFromIModelId = async function (iModelId, options) { - const { exports } = await ITwinPlatform.getExports(iModelId); - const cesiumExport = exports[0]; - if (!defined(cesiumExport)) { - return; - } - return loadExport(cesiumExport, options); }; export default ITwinData; From 990004b98526c55eacddf3ceb639680fb824ec70 Mon Sep 17 00:00:00 2001 From: jjspace <8007967+jjspace@users.noreply.github.com> Date: Fri, 22 Nov 2024 16:52:25 -0500 Subject: [PATCH 127/175] streamline sandcastle with new imodels --- Apps/Sandcastle/gallery/iTwin Demo.html | 138 ++++++++++++++-------- CHANGES.md | 2 +- packages/engine/Source/Scene/ITwinData.js | 1 + 3 files changed, 94 insertions(+), 47 deletions(-) diff --git a/Apps/Sandcastle/gallery/iTwin Demo.html b/Apps/Sandcastle/gallery/iTwin Demo.html index 29a95fe0747a..cab4f922ad4f 100644 --- a/Apps/Sandcastle/gallery/iTwin Demo.html +++ b/Apps/Sandcastle/gallery/iTwin Demo.html @@ -24,24 +24,22 @@

Loading...

- Initializing +
+
diff --git a/Apps/Sandcastle/gallery/iTwin Demo.jpg b/Apps/Sandcastle/gallery/iModel Mesh Export Service.jpg similarity index 100% rename from Apps/Sandcastle/gallery/iTwin Demo.jpg rename to Apps/Sandcastle/gallery/iModel Mesh Export Service.jpg From 0a1c0d67874c2761f615f149f6f1944efbc05ff5 Mon Sep 17 00:00:00 2001 From: jjspace <8007967+jjspace@users.noreply.github.com> Date: Mon, 2 Dec 2024 14:14:08 -0500 Subject: [PATCH 171/175] fix geocode typo --- Apps/Sandcastle/gallery/Clipping Regions.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Apps/Sandcastle/gallery/Clipping Regions.html b/Apps/Sandcastle/gallery/Clipping Regions.html index b7116fc29f17..f56a5c886b27 100644 --- a/Apps/Sandcastle/gallery/Clipping Regions.html +++ b/Apps/Sandcastle/gallery/Clipping Regions.html @@ -43,7 +43,7 @@ animation: false, sceneModePicker: false, baseLayerPicker: false, - geocoder: Cesium.IonGeocoderProviderType.GOOGLE, + geocoder: Cesium.IonGeocodeProviderType.GOOGLE, }); const scene = viewer.scene; From 4628a31c24896abcb395cb09fa98ebc41f1d7276 Mon Sep 17 00:00:00 2001 From: jjspace <8007967+jjspace@users.noreply.github.com> Date: Mon, 2 Dec 2024 11:53:50 -0500 Subject: [PATCH 172/175] update api keys --- packages/engine/Source/Core/Ion.js | 2 +- packages/engine/Source/Scene/ArcGisMapService.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/engine/Source/Core/Ion.js b/packages/engine/Source/Core/Ion.js index 56c3b1d900d0..a0a75ec33486 100644 --- a/packages/engine/Source/Core/Ion.js +++ b/packages/engine/Source/Core/Ion.js @@ -4,7 +4,7 @@ import Resource from "./Resource.js"; let defaultTokenCredit; const defaultAccessToken = - "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJiZjBmMDE4Ny05M2JlLTRlMzgtYjIxYi05YmJjM2QwMzJkYWMiLCJpZCI6MjU5LCJpYXQiOjE3MzA0NjY3MDl9.t-7gCGPUe-oGCyCoeXPtYmlMVdgqUQD9mn-Da23yUoI"; + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI0ZDdmNWJiNy0wMmNlLTQ1MWUtODM2YS02NGM1MTBlOGMwMWQiLCJpZCI6MjU5LCJpYXQiOjE3MzMxNTc4OTV9.B3URHf0VdHDtGckb-hv7uqATdn8KfvkiuoAFZUq8tAo"; /** * Default settings for accessing the Cesium ion API. * diff --git a/packages/engine/Source/Scene/ArcGisMapService.js b/packages/engine/Source/Scene/ArcGisMapService.js index e2f65714ba21..693004b42cd1 100644 --- a/packages/engine/Source/Scene/ArcGisMapService.js +++ b/packages/engine/Source/Scene/ArcGisMapService.js @@ -4,7 +4,7 @@ import Resource from "../Core/Resource.js"; let defaultTokenCredit; const defaultAccessToken = - "AAPTxy8BH1VEsoebNVZXo8HurEOF051kAEKlhkOhBEc9BmQpcUfxe1Yndhf82d5oKkQJ4_7VPaBQGYSISOMaRew7Sy-eTX1JQ4XwaC8v5aCvV72O6LCPs5Ss1pXXH-0uEw6bSRhTeQYHOmikutC2OMyZt6lu0VfT7FA-jVMO_UsunWNTf2cycP2O4IeDN_UV9G-VNmUu2jRvCHioi8o72ua4238s2219cYLEmcoGRJGVJTA.AT1_PjLvyih0"; + "AAPTxy8BH1VEsoebNVZXo8HurEOF051kAEKlhkOhBEc9BmSrZYLHFXe7j_lQcsSJKc8-7rwh0IFSNWLGZErkzXRnYjMjURTz-hGiKMEeAJIZBG7uiYEn0Mt1rrwlJGIpirZQC4iO428519DlO3QC9DnRBqLXGTBhirgoU7-Z2209sy87s49kw6NOC8_Eew6nCLf-pZ883DRPRyAYH7LC8cvRLInud0EdndtUFa4y83TamrA.AT1_ahjrWDrq"; /** * Default options for accessing the ArcGIS image tile service. * From 6ed3a03332d636ce563f5cd647cd6d5da8bd4a5f Mon Sep 17 00:00:00 2001 From: jjspace <8007967+jjspace@users.noreply.github.com> Date: Mon, 2 Dec 2024 13:22:56 -0500 Subject: [PATCH 173/175] reorder changes --- CHANGES.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 88dc99ffabb2..c2fad0a5d1c1 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -9,7 +9,7 @@ - Added an integration with the [iTwin Platform](https://developer.bentley.com/) to load iModels as 3D Tiles. Use `ITwinPlatform.defaultAccessToken` to set the access token. Use `ITwinData.createTilesetFromIModelId(iModelId)` to load the iModel as a `Cesium3DTileset`. [#12289](https://github.com/CesiumGS/cesium/pull/12289) - Added `getSample` to `SampledProperty` to get the time of samples. [#12253](https://github.com/CesiumGS/cesium/pull/12253) - Added `Entity.trackingReferenceFrame` property to allow tracking entities in various reference frames. [#12194](https://github.com/CesiumGS/cesium/pull/12194), [#12314](https://github.com/CesiumGS/cesium/pull/12314) - - `TrackingReferenceFrame.AUTODETECT` (default): uses either VVLH or ENU dependeding on entity's dynamic. Use `TrackingReferenceFrame.ENU` if your camera orientation flips abruptly from time to time. + - `TrackingReferenceFrame.AUTODETECT` (default): uses either VVLH or ENU depending on entity's dynamic. Use `TrackingReferenceFrame.ENU` if your camera orientation flips abruptly from time to time. - `TrackingReferenceFrame.ENU`: uses the entity's local East-North-Up reference frame. - `TrackingReferenceFrame.INERTIAL`: uses the entity's inertial reference frame. - `TrackingReferenceFrame.VELOCITY`: uses entity's `VelocityOrientationProperty` as orientation. @@ -17,16 +17,16 @@ ##### Breaking Changes :mega: +- `PostProcessStageCollection.ambientOcclusion` has been updated with a new algorithm to provide better results at all scales, with tunable performance cost. To approximate the appearance and performance of the old algorithm, set the following values for `scene.postProcessStages.ambientOcclusion.uniforms`: `{ lengthCap: 0.02, directionCount: 6, stepCount: 8 }`. For best results at long distances, consider setting `Viewer.camera.frustum.near` to `1.0` or more, to improve precision in the depth buffer. [#12316](https://github.com/CesiumGS/cesium/pull/12316) - `Rectangle.validate` has been removed. -- `PostProcessStageCollection.ambientOcclusion` has been updated with a new algorithm to provide better results at all scales, with tunable performance cost. To approximate the appearance and performance of the old algorithm, set the following values for `scene.postProcessStages.ambientOcclusion.uniforms`: `{ lengthCap: 0.02, directionCount: 6, stepCount: 8 }`. For best results at long distances, consider setting `Viewer.camera.frustum.near` to `1.0` or more, to improve precision in the depth buffer. ##### Fixes :wrench: - Fixed bug where shared external textures from glTF files were not accounted for in resource statistics. [#12331](https://github.com/CesiumGS/cesium/pull/12331) -- Fix label rendering bug in WebGL1 contexts. [#12301](https://github.com/CesiumGS/cesium/pull/12301) - Fixed lag or crashes when loading many models in the same frame. [#12320](https://github.com/CesiumGS/cesium/pull/12320) -- Updated WMS example URL in UrlTemplateImageryProvider documentation to use an active service. [#12323](https://github.com/CesiumGS/cesium/pull/12323) - Fix point cloud filtering performance on certain hardware [#12317](https://github.com/CesiumGS/cesium/pull/12317) +- Fix label rendering bug in WebGL1 contexts. [#12301](https://github.com/CesiumGS/cesium/pull/12301) +- Updated WMS example URL in UrlTemplateImageryProvider documentation to use an active service. [#12323](https://github.com/CesiumGS/cesium/pull/12323) ##### Deprecated :hourglass_flowing_sand: From 2a7656b0ab9825f5333d0479070b5bbb4fae985f Mon Sep 17 00:00:00 2001 From: jjspace <8007967+jjspace@users.noreply.github.com> Date: Mon, 2 Dec 2024 13:36:26 -0500 Subject: [PATCH 174/175] update package versions --- package.json | 6 +++--- packages/engine/package.json | 2 +- packages/widgets/package.json | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 41eb18d38f80..5f1ed19eb0bd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cesium", - "version": "1.123.1", + "version": "1.124.0", "description": "CesiumJS is a JavaScript library for creating 3D globes and 2D maps in a web browser without a plugin.", "homepage": "http://cesium.com/cesiumjs/", "license": "Apache-2.0", @@ -51,8 +51,8 @@ "./Specs/**/*" ], "dependencies": { - "@cesium/engine": "^12.0.1", - "@cesium/widgets": "^9.0.1" + "@cesium/engine": "^13.0.0", + "@cesium/widgets": "^10.0.0" }, "devDependencies": { "@playwright/test": "^1.41.1", diff --git a/packages/engine/package.json b/packages/engine/package.json index 8d242b4e4350..f95090f5176f 100644 --- a/packages/engine/package.json +++ b/packages/engine/package.json @@ -1,6 +1,6 @@ { "name": "@cesium/engine", - "version": "12.0.1", + "version": "13.0.0", "description": "CesiumJS is a JavaScript library for creating 3D globes and 2D maps in a web browser without a plugin.", "keywords": [ "3D", diff --git a/packages/widgets/package.json b/packages/widgets/package.json index f980eee7f6db..eecf87e2728d 100644 --- a/packages/widgets/package.json +++ b/packages/widgets/package.json @@ -1,6 +1,6 @@ { "name": "@cesium/widgets", - "version": "9.0.1", + "version": "10.0.0", "description": "A widgets library for use with CesiumJS. CesiumJS is a JavaScript library for creating 3D globes and 2D maps in a web browser without a plugin.", "keywords": [ "3D", @@ -28,7 +28,7 @@ "node": ">=14.0.0" }, "dependencies": { - "@cesium/engine": "^12.0.1", + "@cesium/engine": "^13.0.0", "nosleep.js": "^0.12.0" }, "type": "module", From 1d0933221a7d5787a7afe1c40f64133b0748a403 Mon Sep 17 00:00:00 2001 From: jjspace <8007967+jjspace@users.noreply.github.com> Date: Mon, 2 Dec 2024 13:36:59 -0500 Subject: [PATCH 175/175] update thirdparty.json --- ThirdParty.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ThirdParty.json b/ThirdParty.json index 10a55aa1d64e..ed72a070a4c1 100644 --- a/ThirdParty.json +++ b/ThirdParty.json @@ -44,7 +44,7 @@ "license": [ "Apache-2.0" ], - "version": "3.1.7", + "version": "3.2.2", "url": "https://www.npmjs.com/package/dompurify", "notes": "dompurify is available as both MPL-2.0 OR Apache-2.0" },