defineSuite([ 'Core/Cartesian3', 'Core/Cartographic', 'Core/Color', 'Core/Ellipsoid', 'Core/FeatureDetection', 'Core/GeometryInstance', 'Core/Math', 'Core/Matrix4', 'Core/OrthographicFrustum', 'Core/PerspectiveFrustum', 'Core/Ray', 'Core/Rectangle', 'Core/RectangleGeometry', 'Core/ShowGeometryInstanceAttribute', 'Core/Transforms', 'Scene/Cesium3DTileset', 'Scene/Cesium3DTileStyle', 'Scene/EllipsoidSurfaceAppearance', 'Scene/Globe', 'Scene/PointPrimitiveCollection', 'Scene/Primitive', 'Scene/Scene', 'Scene/SceneMode', 'Specs/Cesium3DTilesTester', 'Specs/createCanvas', 'Specs/createScene', 'Specs/pollToPromise' ], 'Scene/Pick', function( Cartesian3, Cartographic, Color, Ellipsoid, FeatureDetection, GeometryInstance, CesiumMath, Matrix4, OrthographicFrustum, PerspectiveFrustum, Ray, Rectangle, RectangleGeometry, ShowGeometryInstanceAttribute, Transforms, Cesium3DTileset, Cesium3DTileStyle, EllipsoidSurfaceAppearance, Globe, PointPrimitiveCollection, Primitive, Scene, SceneMode, Cesium3DTilesTester, createCanvas, createScene, pollToPromise) { 'use strict'; // It's not easily possible to mock the asynchronous pick functions // so don't run those tests when using the WebGL stub var webglStub = !!window.webglStub; var scene; var primitives; var camera; var largeRectangle = Rectangle.fromDegrees(-1.0, -1.0, 1.0, 1.0); var smallRectangle = Rectangle.fromDegrees(-0.0001, -0.0001, 0.0001, 0.0001); var offscreenRectangle = Rectangle.fromDegrees(-45.0002, -1.0002, -45.0001, -1.0001); var primitiveRay; var offscreenRay; beforeAll(function() { scene = createScene({ canvas : createCanvas(10, 10) }); primitives = scene.primitives; camera = scene.camera; camera.setView({ destination : largeRectangle }); primitiveRay = new Ray(camera.positionWC, camera.directionWC); camera.setView({ destination : offscreenRectangle }); offscreenRay = new Ray(camera.positionWC, camera.directionWC); }); afterAll(function() { scene.destroyForSpecs(); }); beforeEach(function() { scene.mode = SceneMode.SCENE3D; scene.morphTime = SceneMode.getMorphTime(scene.mode); camera.setView({ destination : largeRectangle }); camera.frustum = new PerspectiveFrustum(); camera.frustum.fov = CesiumMath.toRadians(60.0); camera.frustum.aspectRatio = 1.0; }); afterEach(function() { primitives.removeAll(); scene.globe = undefined; }); function createRectangle(height, rectangle) { var e = new Primitive({ geometryInstances: new GeometryInstance({ geometry: new RectangleGeometry({ rectangle: rectangle, vertexFormat: EllipsoidSurfaceAppearance.VERTEX_FORMAT, granularity: CesiumMath.toRadians(20.0), height: height }) }), appearance: new EllipsoidSurfaceAppearance({ aboveGround: false }), asynchronous: false }); primitives.add(e); return e; } function createLargeRectangle(height) { return createRectangle(height, largeRectangle); } function createSmallRectangle(height) { return createRectangle(height, smallRectangle); } function createTileset() { var url = 'Data/Cesium3DTiles/Batched/BatchedWithTransformBox/tileset.json'; var options = { maximumScreenSpaceError : 0 }; return Cesium3DTilesTester.loadTileset(scene, url, options).then(function(tileset) { var cartographic = Rectangle.center(largeRectangle); var cartesian = Cartographic.toCartesian(cartographic); tileset.root.transform = Matrix4.IDENTITY; tileset.modelMatrix = Transforms.eastNorthUpToFixedFrame(cartesian); return Cesium3DTilesTester.waitForTilesLoaded(scene, tileset); }); } function createGlobe() { var globe = new Globe(); scene.globe = globe; globe.depthTestAgainstTerrain = true; return pollToPromise(function() { scene.render(); return globe.tilesLoaded; }); } describe('pick', function() { it('does not pick undefined window positions', function() { expect(function() { scene.pick(undefined); }).toThrowDeveloperError(); }); it('picks a primitive', function() { if (FeatureDetection.isInternetExplorer()) { // Workaround IE 11.0.9. This test fails when all tests are ran without a breakpoint here. return; } var rectangle = createLargeRectangle(0.0); expect(scene).toPickPrimitive(rectangle); }); it('picks a primitive with a modified pick search area', function() { if (FeatureDetection.isInternetExplorer()) { // Workaround IE 11.0.9. This test fails when all tests are ran without a breakpoint here. return; } camera.setView({ destination : Rectangle.fromDegrees(-10.0, -10.0, 10.0, 10.0) }); var rectangle = createLargeRectangle(0.0); expect(scene).toPickPrimitive(rectangle, 7, 7, 5); expect(scene).notToPick(7, 7, 3); }); it('does not pick primitives when show is false', function() { var rectangle = createLargeRectangle(0.0); rectangle.show = false; expect(scene).notToPick(); }); it('does not pick primitives when alpha is zero', function() { var rectangle = createLargeRectangle(0.0); rectangle.appearance.material.uniforms.color.alpha = 0.0; expect(scene).notToPick(); }); it('picks the top primitive', function() { createLargeRectangle(0.0); var rectangle2 = createLargeRectangle(1.0); expect(scene).toPickPrimitive(rectangle2); }); it('picks in 2D', function() { scene.morphTo2D(0.0); camera.setView({ destination : largeRectangle }); var rectangle = createLargeRectangle(0.0); scene.initializeFrame(); expect(scene).toPickPrimitive(rectangle); }); it('picks in 3D with orthographic projection', function() { var frustum = new OrthographicFrustum(); frustum.aspectRatio = 1.0; frustum.width = 20.0; camera.frustum = frustum; // force off center update expect(frustum.projectionMatrix).toBeDefined(); camera.setView({ destination : largeRectangle }); var rectangle = createLargeRectangle(0.0); scene.initializeFrame(); expect(scene).toPickPrimitive(rectangle); }); }); describe('drillPick', function() { it('drill picks a primitive with a modified pick search area', function() { if (FeatureDetection.isInternetExplorer()) { // Workaround IE 11.0.9. This test fails when all tests are ran without a breakpoint here. return; } camera.setView({ destination : Rectangle.fromDegrees(-10.0, -10.0, 10.0, 10.0) }); var rectangle = createLargeRectangle(0.0); expect(scene).toDrillPickPrimitive(rectangle, 7, 7, 5); expect(scene).notToDrillPick(7, 7, 3); }); it('does not drill pick undefined window positions', function() { expect(function() { scene.pick(undefined); }).toThrowDeveloperError(); }); it('drill picks multiple objects', function() { var rectangle1 = createLargeRectangle(0.0); var rectangle2 = createLargeRectangle(1.0); expect(scene).toDrillPickAndCall(function(pickedObjects) { expect(pickedObjects.length).toEqual(2); expect(pickedObjects[0].primitive).toEqual(rectangle2); expect(pickedObjects[1].primitive).toEqual(rectangle1); }); }); it('does not drill pick when show is false', function() { var rectangle1 = createLargeRectangle(0.0); var rectangle2 = createLargeRectangle(1.0); rectangle2.show = false; expect(scene).toDrillPickAndCall(function(pickedObjects) { expect(pickedObjects.length).toEqual(1); expect(pickedObjects[0].primitive).toEqual(rectangle1); }); }); it('does not drill pick when alpha is zero', function() { var rectangle1 = createLargeRectangle(0.0); var rectangle2 = createLargeRectangle(1.0); rectangle2.appearance.material.uniforms.color.alpha = 0.0; expect(scene).toDrillPickAndCall(function(pickedObjects) { expect(pickedObjects.length).toEqual(1); expect(pickedObjects[0].primitive).toEqual(rectangle1); }); }); it('can drill pick batched Primitives with show attribute', function() { var geometry = new RectangleGeometry({ rectangle : Rectangle.fromDegrees(-50.0, -50.0, 50.0, 50.0), granularity : CesiumMath.toRadians(20.0), vertexFormat : EllipsoidSurfaceAppearance.VERTEX_FORMAT, height : 0.0 }); var geometryWithHeight = new RectangleGeometry({ rectangle : Rectangle.fromDegrees(-50.0, -50.0, 50.0, 50.0), granularity : CesiumMath.toRadians(20.0), vertexFormat : EllipsoidSurfaceAppearance.VERTEX_FORMAT, height : 20.0 }); var instance1 = new GeometryInstance({ id : 1, geometry : geometry, attributes : { show : new ShowGeometryInstanceAttribute(true) } }); var instance2 = new GeometryInstance({ id : 2, geometry : geometry, attributes : { show : new ShowGeometryInstanceAttribute(false) } }); var instance3 = new GeometryInstance({ id : 3, geometry : geometryWithHeight, attributes : { show : new ShowGeometryInstanceAttribute(true) } }); var primitive = primitives.add(new Primitive({ geometryInstances : [instance1, instance2, instance3], asynchronous : false, appearance : new EllipsoidSurfaceAppearance() })); expect(scene).toDrillPickAndCall(function(pickedObjects) { expect(pickedObjects.length).toEqual(2); expect(pickedObjects[0].primitive).toEqual(primitive); expect(pickedObjects[0].id).toEqual(3); expect(pickedObjects[1].primitive).toEqual(primitive); expect(pickedObjects[1].id).toEqual(1); }); }); it('can drill pick without ID', function() { var geometry = new RectangleGeometry({ rectangle : Rectangle.fromDegrees(-50.0, -50.0, 50.0, 50.0), granularity : CesiumMath.toRadians(20.0), vertexFormat : EllipsoidSurfaceAppearance.VERTEX_FORMAT }); var instance1 = new GeometryInstance({ geometry : geometry, attributes : { show : new ShowGeometryInstanceAttribute(true) } }); var instance2 = new GeometryInstance({ geometry : geometry, attributes : { show : new ShowGeometryInstanceAttribute(true) } }); var primitive = primitives.add(new Primitive({ geometryInstances : [instance1, instance2], asynchronous : false, appearance : new EllipsoidSurfaceAppearance() })); expect(scene).toDrillPickAndCall(function(pickedObjects) { expect(pickedObjects.length).toEqual(1); expect(pickedObjects[0].primitive).toEqual(primitive); }); }); it('can drill pick batched Primitives without show attribute', function() { var geometry = new RectangleGeometry({ rectangle : Rectangle.fromDegrees(-50.0, -50.0, 50.0, 50.0), granularity : CesiumMath.toRadians(20.0), vertexFormat : EllipsoidSurfaceAppearance.VERTEX_FORMAT, height : 0.0 }); var geometryWithHeight = new RectangleGeometry({ rectangle : Rectangle.fromDegrees(-50.0, -50.0, 50.0, 50.0), granularity : CesiumMath.toRadians(20.0), vertexFormat : EllipsoidSurfaceAppearance.VERTEX_FORMAT, height : 20.0 }); var instance1 = new GeometryInstance({ id : 1, geometry : geometry }); var instance2 = new GeometryInstance({ id : 2, geometry : geometry }); var instance3 = new GeometryInstance({ id : 3, geometry : geometryWithHeight }); var primitive = primitives.add(new Primitive({ geometryInstances : [instance1, instance2, instance3], asynchronous : false, appearance : new EllipsoidSurfaceAppearance() })); expect(scene).toDrillPickAndCall(function(pickedObjects) { expect(pickedObjects.length).toEqual(1); expect(pickedObjects[0].primitive).toEqual(primitive); expect(pickedObjects[0].id).toEqual(3); }); }); it('stops drill picking when the limit is reached.', function() { createLargeRectangle(0.0); var rectangle2 = createLargeRectangle(1.0); var rectangle3 = createLargeRectangle(2.0); var rectangle4 = createLargeRectangle(3.0); expect(scene).toDrillPickAndCall(function(pickedObjects) { expect(pickedObjects.length).toEqual(3); expect(pickedObjects[0].primitive).toEqual(rectangle4); expect(pickedObjects[1].primitive).toEqual(rectangle3); expect(pickedObjects[2].primitive).toEqual(rectangle2); }, 3); }); }); function picksFromRayTileset(style) { return createTileset().then(function(tileset) { tileset.style = style; expect(scene).toPickFromRayAndCall(function(result) { var primitive = result.object.primitive; var position = result.position; expect(primitive).toBe(tileset); if (scene.context.depthTexture) { var minimumHeight = Cartesian3.fromRadians(0.0, 0.0).x; var maximumHeight = minimumHeight + 20.0; // Rough height of tile expect(position.x).toBeGreaterThan(minimumHeight); expect(position.x).toBeLessThan(maximumHeight); expect(position.y).toEqualEpsilon(0.0, CesiumMath.EPSILON5); expect(position.z).toEqualEpsilon(0.0, CesiumMath.EPSILON5); } }, primitiveRay); }); } describe('pickFromRay', function() { it('picks a tileset', function() { return picksFromRayTileset(); }); it('picks a translucent tileset', function() { var style = new Cesium3DTileStyle({ color : 'color("white", 0.5)' }); return picksFromRayTileset(style); }); it('picks the globe', function() { if (!scene.context.depthTexture) { return; } return createGlobe().then(function() { expect(scene).toPickFromRayAndCall(function(result) { expect(result.object).toBeUndefined(); expect(result.position).toBeDefined(); expect(result.position.x).toBeGreaterThan(Ellipsoid.WGS84.minimumRadius); expect(result.position.y).toEqualEpsilon(0.0, CesiumMath.EPSILON5); expect(result.position.z).toEqualEpsilon(0.0, CesiumMath.EPSILON5); }, primitiveRay); }); }); it('picks a primitive', function() { var rectangle = createSmallRectangle(0.0); expect(scene).toPickFromRayAndCall(function(result) { var primitive = result.object.primitive; var position = result.position; expect(primitive).toBe(rectangle); if (scene.context.depthTexture) { var expectedPosition = Cartesian3.fromRadians(0.0, 0.0); expect(position).toEqualEpsilon(expectedPosition, CesiumMath.EPSILON5); } }, primitiveRay); }); it('returns undefined if no primitives are picked', function() { createLargeRectangle(0.0); expect(scene).toPickFromRayAndCall(function(result) { expect(result).toBeUndefined(); }, offscreenRay); }); it('does not pick primitives when show is false', function() { var rectangle = createLargeRectangle(0.0); rectangle.show = false; expect(scene).toPickFromRayAndCall(function(result) { expect(result).toBeUndefined(); }, primitiveRay); }); it('does not pick primitives when alpha is zero', function() { var rectangle = createLargeRectangle(0.0); rectangle.appearance.material.uniforms.color.alpha = 0.0; expect(scene).toPickFromRayAndCall(function(result) { expect(result).toBeUndefined(); }, primitiveRay); }); it('picks the top primitive', function() { createLargeRectangle(0.0); var rectangle2 = createLargeRectangle(1.0); expect(scene).toPickFromRayAndCall(function(result) { expect(result.object.primitive).toBe(rectangle2); }, primitiveRay); }); it('excludes objects', function() { var rectangle1 = createLargeRectangle(0.0); var rectangle2 = createLargeRectangle(1.0); var rectangle3 = createLargeRectangle(2.0); var rectangle4 = createLargeRectangle(3.0); rectangle4.show = false; expect(scene).toPickFromRayAndCall(function(result) { expect(result.object.primitive).toBe(rectangle1); }, primitiveRay, [rectangle2, rectangle3, rectangle4]); // Tests that rectangle4 does not get un-hidden expect(scene).toPickFromRayAndCall(function(result) { expect(result.object.primitive).toBe(rectangle3); }, primitiveRay); }); it('picks primitive that doesn\'t write depth', function() { var collection = scene.primitives.add(new PointPrimitiveCollection()); var point = collection.add({ position : Cartographic.fromRadians(0.0, 0.0, 100.0), disableDepthTestDistance : Number.POSITIVE_INFINITY }); expect(scene).toPickFromRayAndCall(function(result) { expect(result.object.primitive).toBe(point); expect(result.position).toBeUndefined(); }, primitiveRay); }); it('throws if ray is undefined', function() { expect(function() { scene.pickFromRay(undefined); }).toThrowDeveloperError(); }); it('throws if scene camera is in 2D', function() { scene.morphTo2D(0.0); expect(function() { scene.pickFromRay(primitiveRay); }).toThrowDeveloperError(); }); it('throws if scene camera is in CV', function() { scene.morphToColumbusView(0.0); expect(function() { scene.pickFromRay(primitiveRay); }).toThrowDeveloperError(); }); }); describe('drillPickFromRay', function() { it('drill picks a primitive', function() { var rectangle = createSmallRectangle(0.0); expect(scene).toDrillPickFromRayAndCall(function(results) { expect(results.length).toBe(1); var primitive = results[0].object.primitive; var position = results[0].position; expect(primitive).toBe(rectangle); if (scene.context.depthTexture) { var expectedPosition = Cartesian3.fromRadians(0.0, 0.0); expect(position).toEqualEpsilon(expectedPosition, CesiumMath.EPSILON5); } else { expect(position).toBeUndefined(); } }, primitiveRay); }); it('drill picks multiple primitives', function() { var rectangle1 = createSmallRectangle(0.0); var rectangle2 = createSmallRectangle(1.0); expect(scene).toDrillPickFromRayAndCall(function(results) { expect(results.length).toBe(2); // rectangle2 is picked before rectangle1 expect(results[0].object.primitive).toBe(rectangle2); expect(results[1].object.primitive).toBe(rectangle1); if (scene.context.depthTexture) { var rectangleCenter1 = Cartesian3.fromRadians(0.0, 0.0, 0.0); var rectangleCenter2 = Cartesian3.fromRadians(0.0, 0.0, 1.0); expect(results[0].position).toEqualEpsilon(rectangleCenter2, CesiumMath.EPSILON5); expect(results[1].position).toEqualEpsilon(rectangleCenter1, CesiumMath.EPSILON5); } else { expect(results[0].position).toBeUndefined(); expect(results[1].position).toBeUndefined(); } }, primitiveRay); }); it('does not drill pick when show is false', function() { var rectangle1 = createLargeRectangle(0.0); var rectangle2 = createLargeRectangle(1.0); rectangle2.show = false; expect(scene).toDrillPickFromRayAndCall(function(results) { expect(results.length).toEqual(1); expect(results[0].object.primitive).toEqual(rectangle1); }, primitiveRay); }); it('does not drill pick when alpha is zero', function() { var rectangle1 = createLargeRectangle(0.0); var rectangle2 = createLargeRectangle(1.0); rectangle2.appearance.material.uniforms.color.alpha = 0.0; expect(scene).toDrillPickFromRayAndCall(function(results) { expect(results.length).toEqual(1); expect(results[0].object.primitive).toEqual(rectangle1); }, primitiveRay); }); it('returns empty array if no primitives are picked', function() { createLargeRectangle(0.0); createLargeRectangle(1.0); expect(scene).toDrillPickFromRayAndCall(function(results) { expect(results.length).toEqual(0); }, offscreenRay); }); it('can drill pick batched Primitives with show attribute', function() { var geometry = new RectangleGeometry({ rectangle : Rectangle.fromDegrees(-50.0, -50.0, 50.0, 50.0), granularity : CesiumMath.toRadians(20.0), vertexFormat : EllipsoidSurfaceAppearance.VERTEX_FORMAT, height : 0.0 }); var geometryWithHeight = new RectangleGeometry({ rectangle : Rectangle.fromDegrees(-50.0, -50.0, 50.0, 50.0), granularity : CesiumMath.toRadians(20.0), vertexFormat : EllipsoidSurfaceAppearance.VERTEX_FORMAT, height : 1.0 }); var instance1 = new GeometryInstance({ id : 1, geometry : geometry, attributes : { show : new ShowGeometryInstanceAttribute(true) } }); var instance2 = new GeometryInstance({ id : 2, geometry : geometry, attributes : { show : new ShowGeometryInstanceAttribute(false) } }); var instance3 = new GeometryInstance({ id : 3, geometry : geometryWithHeight, attributes : { show : new ShowGeometryInstanceAttribute(true) } }); var primitive = primitives.add(new Primitive({ geometryInstances : [instance1, instance2, instance3], asynchronous : false, appearance : new EllipsoidSurfaceAppearance() })); expect(scene).toDrillPickFromRayAndCall(function(results) { expect(results.length).toEqual(2); expect(results[0].object.primitive).toEqual(primitive); expect(results[0].object.id).toEqual(3); expect(results[1].object.primitive).toEqual(primitive); expect(results[1].object.id).toEqual(1); }, primitiveRay); }); it('can drill pick without ID', function() { var geometry = new RectangleGeometry({ rectangle : Rectangle.fromDegrees(-50.0, -50.0, 50.0, 50.0), granularity : CesiumMath.toRadians(20.0), vertexFormat : EllipsoidSurfaceAppearance.VERTEX_FORMAT }); var instance1 = new GeometryInstance({ geometry : geometry, attributes : { show : new ShowGeometryInstanceAttribute(true) } }); var instance2 = new GeometryInstance({ geometry : geometry, attributes : { show : new ShowGeometryInstanceAttribute(true) } }); var primitive = primitives.add(new Primitive({ geometryInstances : [instance1, instance2], asynchronous : false, appearance : new EllipsoidSurfaceAppearance() })); expect(scene).toDrillPickFromRayAndCall(function(results) { expect(results.length).toEqual(1); expect(results[0].object.primitive).toEqual(primitive); }, primitiveRay); }); it('can drill pick batched Primitives without show attribute', function() { var geometry = new RectangleGeometry({ rectangle : Rectangle.fromDegrees(-50.0, -50.0, 50.0, 50.0), granularity : CesiumMath.toRadians(20.0), vertexFormat : EllipsoidSurfaceAppearance.VERTEX_FORMAT, height : 0.0 }); var geometryWithHeight = new RectangleGeometry({ rectangle : Rectangle.fromDegrees(-50.0, -50.0, 50.0, 50.0), granularity : CesiumMath.toRadians(20.0), vertexFormat : EllipsoidSurfaceAppearance.VERTEX_FORMAT, height : 1.0 }); var instance1 = new GeometryInstance({ id : 1, geometry : geometry }); var instance2 = new GeometryInstance({ id : 2, geometry : geometry }); var instance3 = new GeometryInstance({ id : 3, geometry : geometryWithHeight }); var primitive = primitives.add(new Primitive({ geometryInstances : [instance1, instance2, instance3], asynchronous : false, appearance : new EllipsoidSurfaceAppearance() })); expect(scene).toDrillPickFromRayAndCall(function(results) { expect(results.length).toEqual(1); expect(results[0].object.primitive).toEqual(primitive); expect(results[0].object.id).toEqual(3); }, primitiveRay); }); it('stops drill picking when the limit is reached.', function() { createLargeRectangle(0.0); var rectangle2 = createLargeRectangle(1.0); var rectangle3 = createLargeRectangle(2.0); var rectangle4 = createLargeRectangle(3.0); expect(scene).toDrillPickFromRayAndCall(function(results) { expect(results.length).toEqual(3); expect(results[0].object.primitive).toEqual(rectangle4); expect(results[1].object.primitive).toEqual(rectangle3); expect(results[2].object.primitive).toEqual(rectangle2); }, primitiveRay, 3); }); it('excludes objects', function() { createLargeRectangle(0.0); var rectangle2 = createLargeRectangle(1.0); var rectangle3 = createLargeRectangle(2.0); var rectangle4 = createLargeRectangle(3.0); var rectangle5 = createLargeRectangle(4.0); expect(scene).toDrillPickFromRayAndCall(function(results) { expect(results.length).toBe(2); expect(results[0].object.primitive).toBe(rectangle4); expect(results[1].object.primitive).toBe(rectangle2); }, primitiveRay, 2, [rectangle5, rectangle3]); }); it('throws if ray is undefined', function() { expect(function() { scene.drillPickFromRay(undefined); }).toThrowDeveloperError(); }); it('throws if scene camera is in 2D', function() { scene.morphTo2D(0.0); expect(function() { scene.drillPickFromRay(primitiveRay); }).toThrowDeveloperError(); }); it('throws if scene camera is in CV', function() { scene.morphToColumbusView(0.0); expect(function() { scene.drillPickFromRay(primitiveRay); }).toThrowDeveloperError(); }); }); describe('sampleHeight', function() { it('samples height from tileset', function() { if (!scene.sampleHeightSupported) { return; } var cartographic = new Cartographic(0.0, 0.0); return createTileset().then(function(tileset) { expect(scene).toSampleHeightAndCall(function(height) { expect(height).toBeGreaterThan(0.0); expect(height).toBeLessThan(20.0); // Rough height of tile }, cartographic); }); }); it('samples height from the globe', function() { if (!scene.sampleHeightSupported) { return; } var cartographic = new Cartographic(0.0, 0.0); return createGlobe().then(function() { expect(scene).toSampleHeightAndCall(function(height) { expect(height).toBeDefined(); }, cartographic); }); }); it('samples height from primitive', function() { if (!scene.sampleHeightSupported) { return; } createSmallRectangle(0.0); var cartographic = new Cartographic(0.0, 0.0); expect(scene).toSampleHeightAndCall(function(height) { expect(height).toEqualEpsilon(0.0, CesiumMath.EPSILON3); }, cartographic); }); it('samples height from the top primitive', function() { if (!scene.sampleHeightSupported) { return; } createSmallRectangle(0.0); createSmallRectangle(1.0); var cartographic = new Cartographic(0.0, 0.0); expect(scene).toSampleHeightAndCall(function(height) { expect(height).toEqualEpsilon(1.0, CesiumMath.EPSILON3); }, cartographic); }); it('returns undefined if no height is sampled', function() { if (!scene.sampleHeightSupported) { return; } createSmallRectangle(0.0); var cartographic = new Cartographic(1.0, 0.0); expect(scene).toSampleHeightAndCall(function(height) { expect(height).toBeUndefined(); }, cartographic); }); it('excludes objects', function() { if (!scene.sampleHeightSupported) { return; } createSmallRectangle(0.0); var rectangle2 = createSmallRectangle(1.0); var rectangle3 = createSmallRectangle(2.0); var cartographic = new Cartographic(0.0, 0.0); expect(scene).toSampleHeightAndCall(function(height) { expect(height).toEqualEpsilon(0.0, CesiumMath.EPSILON3); }, cartographic, [rectangle2, rectangle3]); }); it('excludes primitive that doesn\'t write depth', function() { if (!scene.sampleHeightSupported) { return; } var rectangle = createSmallRectangle(0.0); var height = 100.0; var cartographic = new Cartographic(0.0, 0.0, height); var collection = scene.primitives.add(new PointPrimitiveCollection()); var point = collection.add({ position : Cartographic.toCartesian(cartographic) }); expect(scene).toSampleHeightAndCall(function(height) { expect(height).toEqualEpsilon(height, CesiumMath.EPSILON3); }, cartographic); point.disableDepthTestDistance = Number.POSITIVE_INFINITY; expect(scene).toSampleHeightAndCall(function(height) { expect(height).toEqualEpsilon(0.0, CesiumMath.EPSILON3); }, cartographic); rectangle.show = false; expect(scene).toSampleHeightAndCall(function(height) { expect(height).toBeUndefined(); }, cartographic); }); it('throws if position is undefined', function() { if (!scene.sampleHeightSupported) { return; } expect(function() { scene.sampleHeight(undefined); }).toThrowDeveloperError(); }); it('throws if scene camera is in 2D', function() { if (!scene.sampleHeightSupported) { return; } scene.morphTo2D(0.0); var cartographic = new Cartographic(0.0, 0.0); expect(function() { scene.sampleHeight(cartographic); }).toThrowDeveloperError(); }); it('throws if scene camera is in CV', function() { if (!scene.sampleHeightSupported) { return; } scene.morphToColumbusView(0.0); var cartographic = new Cartographic(0.0, 0.0); expect(function() { scene.sampleHeight(cartographic); }).toThrowDeveloperError(); }); it('throws if sampleHeight is not supported', function() { if (!scene.sampleHeightSupported) { return; } // Disable extension var depthTexture = scene.context._depthTexture; scene.context._depthTexture = false; var cartographic = new Cartographic(0.0, 0.0); expect(function() { scene.sampleHeight(cartographic); }).toThrowDeveloperError(); // Re-enable extension scene.context._depthTexture = depthTexture; }); }); describe('clampToHeight', function() { it('clamps to tileset', function() { if (!scene.clampToHeightSupported) { return; } var cartesian = Cartesian3.fromRadians(0.0, 0.0, 100000.0); return createTileset().then(function(tileset) { expect(scene).toClampToHeightAndCall(function(position) { var minimumHeight = Cartesian3.fromRadians(0.0, 0.0).x; var maximumHeight = minimumHeight + 20.0; // Rough height of tile expect(position.x).toBeGreaterThan(minimumHeight); expect(position.x).toBeLessThan(maximumHeight); expect(position.y).toEqualEpsilon(0.0, CesiumMath.EPSILON5); expect(position.z).toEqualEpsilon(0.0, CesiumMath.EPSILON5); }, cartesian); }); }); it('clamps to the globe', function() { if (!scene.clampToHeightSupported) { return; } var cartesian = Cartesian3.fromRadians(0.0, 0.0, 100000.0); return createGlobe().then(function() { expect(scene).toClampToHeightAndCall(function(position) { expect(position).toBeDefined(); }, cartesian); }); }); it('clamps to primitive', function() { if (!scene.clampToHeightSupported) { return; } createSmallRectangle(0.0); var cartesian = Cartesian3.fromRadians(0.0, 0.0, 100000.0); expect(scene).toClampToHeightAndCall(function(cartesian) { var expectedCartesian = Cartesian3.fromRadians(0.0, 0.0); expect(cartesian).toEqualEpsilon(expectedCartesian, CesiumMath.EPSILON5); }, cartesian); }); it('clamps to top primitive', function() { if (!scene.clampToHeightSupported) { return; } createSmallRectangle(0.0); createSmallRectangle(1.0); var cartesian = Cartesian3.fromRadians(0.0, 0.0, 100000.0); expect(scene).toClampToHeightAndCall(function(cartesian) { var expectedCartesian = Cartesian3.fromRadians(0.0, 0.0, 1.0); expect(cartesian).toEqualEpsilon(expectedCartesian, CesiumMath.EPSILON5); }, cartesian); }); it('returns undefined if there was nothing to clamp to', function() { if (!scene.clampToHeightSupported) { return; } createSmallRectangle(0.0); var cartesian = Cartesian3.fromRadians(1.0, 0.0, 100000.0); expect(scene).toClampToHeightAndCall(function(cartesian) { expect(cartesian).toBeUndefined(); }, cartesian); }); it('excludes objects', function() { if (!scene.clampToHeightSupported) { return; } createSmallRectangle(0.0); var rectangle2 = createSmallRectangle(1.0); var rectangle3 = createSmallRectangle(2.0); var cartesian = Cartesian3.fromRadians(0.0, 0.0, 100000.0); expect(scene).toClampToHeightAndCall(function(cartesian) { var expectedCartesian = Cartesian3.fromRadians(0.0, 0.0); expect(cartesian).toEqualEpsilon(expectedCartesian, CesiumMath.EPSILON5); }, cartesian, [rectangle2, rectangle3]); }); it('excludes primitive that doesn\'t write depth', function() { if (!scene.clampToHeightSupported) { return; } var rectangle = createSmallRectangle(0.0); var cartesian = Cartesian3.fromRadians(0.0, 0.0, 100.0); var collection = scene.primitives.add(new PointPrimitiveCollection()); var point = collection.add({ position : cartesian }); expect(scene).toClampToHeightAndCall(function(clampedCartesian) { expect(clampedCartesian).toEqualEpsilon(cartesian, CesiumMath.EPSILON3); }, cartesian); point.disableDepthTestDistance = Number.POSITIVE_INFINITY; expect(scene).toClampToHeightAndCall(function(clampedCartesian) { expect(clampedCartesian).toEqualEpsilon(cartesian, CesiumMath.EPSILON3); }, cartesian); rectangle.show = false; expect(scene).toClampToHeightAndCall(function(clampedCartesian) { expect(clampedCartesian).toBeUndefined(); }, cartesian); }); it('throws if cartesian is undefined', function() { if (!scene.clampToHeightSupported) { return; } expect(function() { scene.clampToHeight(undefined); }).toThrowDeveloperError(); }); it('throws if scene camera is in 2D', function() { if (!scene.clampToHeightSupported) { return; } scene.morphTo2D(0.0); var cartesian = Cartesian3.fromRadians(0.0, 0.0, 100000.0); expect(function() { scene.clampToHeight(cartesian); }).toThrowDeveloperError(); }); it('throws if scene camera is in CV', function() { if (!scene.clampToHeightSupported) { return; } scene.morphToColumbusView(0.0); var cartesian = Cartesian3.fromRadians(0.0, 0.0, 100000.0); expect(function() { scene.clampToHeight(cartesian); }).toThrowDeveloperError(); }); it('throws if clampToHeight is not supported', function() { if (!scene.clampToHeightSupported) { return; } // Disable extension var depthTexture = scene.context._depthTexture; scene.context._depthTexture = false; var cartesian = Cartesian3.fromRadians(0.0, 0.0, 100000.0); expect(function() { scene.clampToHeight(cartesian); }).toThrowDeveloperError(); // Re-enable extension scene.context._depthTexture = depthTexture; }); }); function pickFromRayMostDetailed(ray, objectsToExclude) { var result; var completed = false; scene.pickFromRayMostDetailed(ray, objectsToExclude).then(function(pickResult) { result = pickResult; completed = true; }); return pollToPromise(function() { // Scene requires manual updates in the tests to move along the promise scene.render(); return completed; }).then(function() { return result; }); } function drillPickFromRayMostDetailed(ray, limit, objectsToExclude) { var result; var completed = false; scene.drillPickFromRayMostDetailed(ray, limit, objectsToExclude).then(function(pickResult) { result = pickResult; completed = true; }); return pollToPromise(function() { // Scene requires manual updates in the tests to move along the promise scene.render(); return completed; }).then(function() { return result; }); } function sampleHeightMostDetailed(cartographics, objectsToExclude) { var result; var completed = false; scene.sampleHeightMostDetailed(cartographics, objectsToExclude).then(function(pickResult) { result = pickResult; completed = true; }); return pollToPromise(function() { // Scene requires manual updates in the tests to move along the promise scene.render(); return completed; }).then(function() { return result; }); } function clampToHeightMostDetailed(cartesians, objectsToExclude) { var result; var completed = false; scene.clampToHeightMostDetailed(cartesians, objectsToExclude).then(function(pickResult) { result = pickResult; completed = true; }); return pollToPromise(function() { // Scene requires manual updates in the tests to move along the promise scene.render(); return completed; }).then(function() { return result; }); } describe('pickFromRayMostDetailed', function() { it('picks a tileset', function() { if (webglStub) { return; } scene.camera.setView({ destination : offscreenRectangle }); return createTileset().then(function(tileset) { return pickFromRayMostDetailed(primitiveRay).then(function(result) { var primitive = result.object.primitive; var position = result.position; expect(primitive).toBe(tileset); if (scene.context.depthTexture) { var minimumHeight = Cartesian3.fromRadians(0.0, 0.0).x; var maximumHeight = minimumHeight + 20.0; // Rough height of tile expect(position.x).toBeGreaterThan(minimumHeight); expect(position.x).toBeLessThan(maximumHeight); expect(position.y).toEqualEpsilon(0.0, CesiumMath.EPSILON5); expect(position.z).toEqualEpsilon(0.0, CesiumMath.EPSILON5); } }); }); }); it('excludes tileset in objectsToExclude list', function() { if (webglStub) { return; } scene.camera.setView({ destination : offscreenRectangle }); return createTileset().then(function(tileset) { var objectsToExclude = [tileset]; return pickFromRayMostDetailed(primitiveRay, objectsToExclude).then(function(result) { expect(result).toBeUndefined(); }); }); }); it('excludes tileset whose show is false', function() { if (webglStub) { return; } scene.camera.setView({ destination : offscreenRectangle }); return createTileset().then(function(tileset) { tileset.show = false; return pickFromRayMostDetailed(primitiveRay).then(function(result) { expect(result).toBeUndefined(); }); }); }); it('picks a primitive', function() { if (webglStub) { return; } var rectangle = createSmallRectangle(0.0); scene.camera.setView({ destination : offscreenRectangle }); return pickFromRayMostDetailed(primitiveRay).then(function(result) { var primitive = result.object.primitive; var position = result.position; expect(primitive).toBe(rectangle); if (scene.context.depthTexture) { var expectedPosition = Cartesian3.fromRadians(0.0, 0.0); expect(position).toEqualEpsilon(expectedPosition, CesiumMath.EPSILON5); } }); }); it('returns undefined if no primitives are picked', function() { if (webglStub) { return; } createLargeRectangle(0.0); scene.camera.setView({ destination : offscreenRectangle }); return pickFromRayMostDetailed(offscreenRay).then(function(result) { expect(result).toBeUndefined(); }); }); it('picks the top primitive', function() { if (webglStub) { return; } createLargeRectangle(0.0); var rectangle2 = createLargeRectangle(1.0); scene.camera.setView({ destination : offscreenRectangle }); return pickFromRayMostDetailed(primitiveRay).then(function(result) { expect(result.object.primitive).toBe(rectangle2); }); }); it('excludes objects', function() { if (webglStub) { return; } var rectangle1 = createLargeRectangle(0.0); var rectangle2 = createLargeRectangle(1.0); var rectangle3 = createLargeRectangle(2.0); var rectangle4 = createLargeRectangle(3.0); rectangle4.show = false; scene.camera.setView({ destination : offscreenRectangle }); return pickFromRayMostDetailed(primitiveRay, [rectangle2, rectangle3, rectangle4]).then(function(result) { expect(result.object.primitive).toBe(rectangle1); }).then(function() { return pickFromRayMostDetailed(primitiveRay).then(function(result) { expect(result.object.primitive).toBe(rectangle3); }); }); }); it('picks primitive that doesn\'t write depth', function() { if (webglStub) { return; } var collection = scene.primitives.add(new PointPrimitiveCollection()); var point = collection.add({ position : Cartographic.fromRadians(0.0, 0.0, 100.0), disableDepthTestDistance : Number.POSITIVE_INFINITY }); scene.camera.setView({ destination : offscreenRectangle }); return pickFromRayMostDetailed(primitiveRay).then(function(result) { expect(result.object.primitive).toBe(point); expect(result.position).toBeUndefined(); }); }); it('throws if ray is undefined', function() { expect(function() { scene.pickFromRayMostDetailed(undefined); }).toThrowDeveloperError(); }); it('throws if scene camera is in 2D', function() { scene.morphTo2D(0.0); expect(function() { scene.pickFromRayMostDetailed(undefined); }).toThrowDeveloperError(); }); it('throws if scene camera is in CV', function() { scene.morphToColumbusView(0.0); expect(function() { scene.pickFromRayMostDetailed(undefined); }).toThrowDeveloperError(); }); }); describe('drillPickFromRayMostDetailed', function() { it('drill picks a primitive', function() { if (webglStub) { return; } var rectangle = createSmallRectangle(0.0); scene.camera.setView({ destination : offscreenRectangle }); return drillPickFromRayMostDetailed(primitiveRay).then(function(results) { expect(results.length).toBe(1); var primitive = results[0].object.primitive; var position = results[0].position; expect(primitive).toBe(rectangle); if (scene.context.depthTexture) { var expectedPosition = Cartesian3.fromRadians(0.0, 0.0); expect(position).toEqualEpsilon(expectedPosition, CesiumMath.EPSILON5); } else { expect(position).toBeUndefined(); } }); }); it('drill picks multiple primitives', function() { if (webglStub) { return; } var rectangle1 = createSmallRectangle(0.0); var rectangle2 = createSmallRectangle(1.0); scene.camera.setView({ destination : offscreenRectangle }); return drillPickFromRayMostDetailed(primitiveRay).then(function(results) { expect(results.length).toBe(2); // rectangle2 is picked before rectangle1 expect(results[0].object.primitive).toBe(rectangle2); expect(results[1].object.primitive).toBe(rectangle1); if (scene.context.depthTexture) { var rectangleCenter1 = Cartesian3.fromRadians(0.0, 0.0, 0.0); var rectangleCenter2 = Cartesian3.fromRadians(0.0, 0.0, 1.0); expect(results[0].position).toEqualEpsilon(rectangleCenter2, CesiumMath.EPSILON5); expect(results[1].position).toEqualEpsilon(rectangleCenter1, CesiumMath.EPSILON5); } else { expect(results[0].position).toBeUndefined(); expect(results[1].position).toBeUndefined(); } }); }); it('does not drill pick when show is false', function() { if (webglStub) { return; } var rectangle1 = createLargeRectangle(0.0); var rectangle2 = createLargeRectangle(1.0); rectangle2.show = false; scene.camera.setView({ destination : offscreenRectangle }); return drillPickFromRayMostDetailed(primitiveRay).then(function(results) { expect(results.length).toEqual(1); expect(results[0].object.primitive).toEqual(rectangle1); }); }); it('does not drill pick when alpha is zero', function() { if (webglStub) { return; } var rectangle1 = createLargeRectangle(0.0); var rectangle2 = createLargeRectangle(1.0); rectangle2.appearance.material.uniforms.color.alpha = 0.0; scene.camera.setView({ destination : offscreenRectangle }); return drillPickFromRayMostDetailed(primitiveRay).then(function(results) { expect(results.length).toEqual(1); expect(results[0].object.primitive).toEqual(rectangle1); }); }); it('returns empty array if no primitives are picked', function() { if (webglStub) { return; } createLargeRectangle(0.0); createLargeRectangle(1.0); scene.camera.setView({ destination : offscreenRectangle }); return drillPickFromRayMostDetailed(offscreenRay).then(function(results) { expect(results.length).toEqual(0); }); }); it('can drill pick batched Primitives with show attribute', function() { if (webglStub) { return; } var geometry = new RectangleGeometry({ rectangle : Rectangle.fromDegrees(-50.0, -50.0, 50.0, 50.0), granularity : CesiumMath.toRadians(20.0), vertexFormat : EllipsoidSurfaceAppearance.VERTEX_FORMAT, height : 0.0 }); var geometryWithHeight = new RectangleGeometry({ rectangle : Rectangle.fromDegrees(-50.0, -50.0, 50.0, 50.0), granularity : CesiumMath.toRadians(20.0), vertexFormat : EllipsoidSurfaceAppearance.VERTEX_FORMAT, height : 1.0 }); var instance1 = new GeometryInstance({ id : 1, geometry : geometry, attributes : { show : new ShowGeometryInstanceAttribute(true) } }); var instance2 = new GeometryInstance({ id : 2, geometry : geometry, attributes : { show : new ShowGeometryInstanceAttribute(false) } }); var instance3 = new GeometryInstance({ id : 3, geometry : geometryWithHeight, attributes : { show : new ShowGeometryInstanceAttribute(true) } }); var primitive = primitives.add(new Primitive({ geometryInstances : [instance1, instance2, instance3], asynchronous : false, appearance : new EllipsoidSurfaceAppearance() })); scene.camera.setView({ destination : offscreenRectangle }); return drillPickFromRayMostDetailed(primitiveRay).then(function(results) { expect(results.length).toEqual(2); expect(results[0].object.primitive).toEqual(primitive); expect(results[0].object.id).toEqual(3); expect(results[1].object.primitive).toEqual(primitive); expect(results[1].object.id).toEqual(1); }); }); it('can drill pick without ID', function() { if (webglStub) { return; } var geometry = new RectangleGeometry({ rectangle : Rectangle.fromDegrees(-50.0, -50.0, 50.0, 50.0), granularity : CesiumMath.toRadians(20.0), vertexFormat : EllipsoidSurfaceAppearance.VERTEX_FORMAT }); var instance1 = new GeometryInstance({ geometry : geometry, attributes : { show : new ShowGeometryInstanceAttribute(true) } }); var instance2 = new GeometryInstance({ geometry : geometry, attributes : { show : new ShowGeometryInstanceAttribute(true) } }); var primitive = primitives.add(new Primitive({ geometryInstances : [instance1, instance2], asynchronous : false, appearance : new EllipsoidSurfaceAppearance() })); scene.camera.setView({ destination : offscreenRectangle }); return drillPickFromRayMostDetailed(primitiveRay).then(function(results) { expect(results.length).toEqual(1); expect(results[0].object.primitive).toEqual(primitive); }); }); it('can drill pick batched Primitives without show attribute', function() { if (webglStub) { return; } var geometry = new RectangleGeometry({ rectangle : Rectangle.fromDegrees(-50.0, -50.0, 50.0, 50.0), granularity : CesiumMath.toRadians(20.0), vertexFormat : EllipsoidSurfaceAppearance.VERTEX_FORMAT, height : 0.0 }); var geometryWithHeight = new RectangleGeometry({ rectangle : Rectangle.fromDegrees(-50.0, -50.0, 50.0, 50.0), granularity : CesiumMath.toRadians(20.0), vertexFormat : EllipsoidSurfaceAppearance.VERTEX_FORMAT, height : 1.0 }); var instance1 = new GeometryInstance({ id : 1, geometry : geometry }); var instance2 = new GeometryInstance({ id : 2, geometry : geometry }); var instance3 = new GeometryInstance({ id : 3, geometry : geometryWithHeight }); var primitive = primitives.add(new Primitive({ geometryInstances : [instance1, instance2, instance3], asynchronous : false, appearance : new EllipsoidSurfaceAppearance() })); scene.camera.setView({ destination : offscreenRectangle }); return drillPickFromRayMostDetailed(primitiveRay).then(function(results) { expect(results.length).toEqual(1); expect(results[0].object.primitive).toEqual(primitive); expect(results[0].object.id).toEqual(3); }); }); it('stops drill picking when the limit is reached.', function() { if (webglStub) { return; } createLargeRectangle(0.0); var rectangle2 = createLargeRectangle(1.0); var rectangle3 = createLargeRectangle(2.0); var rectangle4 = createLargeRectangle(3.0); scene.camera.setView({ destination : offscreenRectangle }); return drillPickFromRayMostDetailed(primitiveRay, 3).then(function(results) { expect(results.length).toEqual(3); expect(results[0].object.primitive).toEqual(rectangle4); expect(results[1].object.primitive).toEqual(rectangle3); expect(results[2].object.primitive).toEqual(rectangle2); }); }); it('excludes objects', function() { if (webglStub) { return; } createLargeRectangle(0.0); var rectangle2 = createLargeRectangle(1.0); var rectangle3 = createLargeRectangle(2.0); var rectangle4 = createLargeRectangle(3.0); var rectangle5 = createLargeRectangle(4.0); scene.camera.setView({ destination : offscreenRectangle }); return drillPickFromRayMostDetailed(primitiveRay, 2, [rectangle5, rectangle3]).then(function(results) { expect(results.length).toBe(2); expect(results[0].object.primitive).toBe(rectangle4); expect(results[1].object.primitive).toBe(rectangle2); }); }); it('throws if ray is undefined', function() { expect(function() { scene.drillPickFromRayMostDetailed(undefined); }).toThrowDeveloperError(); }); it('throws if scene camera is in 2D', function() { scene.morphTo2D(0.0); expect(function() { scene.drillPickFromRayMostDetailed(primitiveRay); }).toThrowDeveloperError(); }); it('throws if scene camera is in CV', function() { scene.morphToColumbusView(0.0); expect(function() { scene.drillPickFromRayMostDetailed(primitiveRay); }).toThrowDeveloperError(); }); }); describe('sampleHeightMostDetailed', function() { it('samples height from tileset', function() { if (!scene.sampleHeightSupported) { return; } var cartographics = [new Cartographic(0.0, 0.0)]; return createTileset().then(function() { return sampleHeightMostDetailed(cartographics).then(function(updatedCartographics) { var height = updatedCartographics[0].height; expect(height).toBeGreaterThan(0.0); expect(height).toBeLessThan(20.0); // Rough height of tile }); }); }); it('samples height from the globe', function() { if (!scene.sampleHeightSupported) { return; } var cartographics = [ new Cartographic(0.0, 0.0), new Cartographic(0.0001, 0.0001), new Cartographic(0.0002, 0.0002) ]; var clonedCartographics = [new Cartographic(0.0, 0.0), new Cartographic(0.0001, 0.0001), new Cartographic(0.0002, 0.0002)]; return createGlobe().then(function() { return sampleHeightMostDetailed(cartographics).then(function(updatedCartographics) { expect(updatedCartographics).toBe(cartographics); expect(updatedCartographics.length).toBe(3); var previousHeight; for (var i = 0; i < 3; ++i) { var longitude = updatedCartographics[i].longitude; var latitude = updatedCartographics[i].latitude; var height = updatedCartographics[i].height; expect(longitude).toBe(clonedCartographics[i].longitude); expect(latitude).toBe(clonedCartographics[i].latitude); expect(height).toBeDefined(); expect(height).not.toBe(previousHeight); previousHeight = height; } }); }); }); it('does not sample offscreen globe tiles', function() { if (!scene.sampleHeightSupported) { return; } var cartographics = [new Cartographic(0.0, 0.0)]; scene.camera.setView({ destination : offscreenRectangle }); return createGlobe().then(function() { return sampleHeightMostDetailed(cartographics).then(function(updatedCartographics) { expect(updatedCartographics[0].height).toBeUndefined(); }); }); }); it('samples height from multiple primitives', function() { if (!scene.sampleHeightSupported) { return; } createRectangle(0.0, smallRectangle); createRectangle(0.0, offscreenRectangle); var cartographics = [ Rectangle.center(smallRectangle), Rectangle.center(offscreenRectangle), new Cartographic(-2.0, -2.0) ]; scene.camera.setView({ destination : offscreenRectangle }); return sampleHeightMostDetailed(cartographics).then(function(updatedCartographics) { expect(updatedCartographics[0].height).toBeDefined(); expect(updatedCartographics[1].height).toBeDefined(); expect(updatedCartographics[2].height).toBeUndefined(); // No primitive occupies this space }); }); it('samples multiple heights from primitive', function() { if (!scene.sampleHeightSupported) { return; } createSmallRectangle(0.0); var cartographics = [ new Cartographic(0.0, 0.0), new Cartographic(-0.000001, -0.000001), new Cartographic(0.0000005, 0.0000005) ]; scene.camera.setView({ destination : offscreenRectangle }); return sampleHeightMostDetailed(cartographics).then(function(updatedCartographics) { var previousHeight; for (var i = 0; i < 3; ++i) { var height = updatedCartographics[i].height; expect(height).toEqualEpsilon(0.0, CesiumMath.EPSILON3); expect(height).not.toBe(previousHeight); previousHeight = height; } }); }); it('samples height from the top primitive', function() { if (!scene.sampleHeightSupported) { return; } createSmallRectangle(0.0); createSmallRectangle(1.0); var cartographics = [new Cartographic(0.0, 0.0)]; scene.camera.setView({ destination : offscreenRectangle }); return sampleHeightMostDetailed(cartographics).then(function(updatedCartographics) { expect(updatedCartographics[0].height).toEqualEpsilon(1.0, CesiumMath.EPSILON3); }); }); it('excludes objects', function() { if (!scene.sampleHeightSupported) { return; } var rectangle1 = createRectangle(0.0, smallRectangle); createRectangle(0.0, offscreenRectangle); var rectangle3 = createRectangle(1.0, offscreenRectangle); var cartographics = [ Rectangle.center(smallRectangle), Rectangle.center(offscreenRectangle), new Cartographic(-2.0, -2.0) ]; scene.camera.setView({ destination : offscreenRectangle }); return sampleHeightMostDetailed(cartographics, [rectangle1, rectangle3]).then(function(updatedCartographics) { expect(updatedCartographics[0].height).toBeUndefined(); // This rectangle was excluded expect(updatedCartographics[1].height).toEqualEpsilon(0.0, CesiumMath.EPSILON2); expect(updatedCartographics[2].height).toBeUndefined(); // No primitive occupies this space }); }); it('excludes primitive that doesn\'t write depth', function() { if (!scene.sampleHeightSupported) { return; } var rectangle = createSmallRectangle(0.0); var height = 100.0; var cartographics = [new Cartographic(0.0, 0.0, height)]; var collection = scene.primitives.add(new PointPrimitiveCollection()); var point = collection.add({ position : Cartographic.toCartesian(cartographics[0]) }); scene.camera.setView({ destination : offscreenRectangle }); return sampleHeightMostDetailed(cartographics).then(function(updatedCartographics) { expect(updatedCartographics[0].height).toEqualEpsilon(height, CesiumMath.EPSILON3); }).then(function() { point.disableDepthTestDistance = Number.POSITIVE_INFINITY; return sampleHeightMostDetailed(cartographics).then(function(updatedCartographics) { expect(updatedCartographics[0].height).toEqualEpsilon(0.0, CesiumMath.EPSILON3); }); }).then(function() { rectangle.show = false; return sampleHeightMostDetailed(cartographics).then(function(updatedCartographics) { expect(updatedCartographics[0].height).toBeUndefined(); }); }); }); it('handles empty array', function() { if (!scene.sampleHeightSupported) { return; } var cartographics = []; return sampleHeightMostDetailed(cartographics).then(function(updatedCartographics) { expect(updatedCartographics.length).toBe(0); }); }); it('throws if positions is undefined', function() { if (!scene.sampleHeightSupported) { return; } expect(function() { scene.sampleHeightMostDetailed(undefined); }).toThrowDeveloperError(); }); it('throws if scene camera is in 2D', function() { if (!scene.sampleHeightSupported) { return; } scene.morphTo2D(0.0); var cartographics = [new Cartographic(0.0, 0.0)]; expect(function() { scene.sampleHeightMostDetailed(cartographics); }).toThrowDeveloperError(); }); it('throws if scene camera is in CV', function() { if (!scene.sampleHeightSupported) { return; } scene.morphToColumbusView(0.0); var cartographics = [new Cartographic(0.0, 0.0)]; expect(function() { scene.sampleHeightMostDetailed(cartographics); }).toThrowDeveloperError(); }); it('throws if sampleHeight is not supported', function() { if (!scene.sampleHeightSupported) { return; } // Disable extension var depthTexture = scene.context._depthTexture; scene.context._depthTexture = false; var cartographics = [new Cartographic(0.0, 0.0)]; expect(function() { scene.sampleHeightMostDetailed(cartographics); }).toThrowDeveloperError(); // Re-enable extension scene.context._depthTexture = depthTexture; }); }); describe('clampToHeightMostDetailed', function() { it('clamps to tileset', function() { if (!scene.clampToHeightSupported) { return; } var cartesians = [Cartesian3.fromRadians(0.0, 0.0, 100000.0)]; return createTileset().then(function() { return clampToHeightMostDetailed(cartesians).then(function(updatedCartesians) { var minimumHeight = Cartesian3.fromRadians(0.0, 0.0).x; var maximumHeight = minimumHeight + 20.0; // Rough height of tile var position = updatedCartesians[0]; expect(position.x).toBeGreaterThan(minimumHeight); expect(position.x).toBeLessThan(maximumHeight); expect(position.y).toEqualEpsilon(0.0, CesiumMath.EPSILON5); expect(position.z).toEqualEpsilon(0.0, CesiumMath.EPSILON5); }); }); }); it('clamps to the globe', function() { if (!scene.clampToHeightSupported) { return; } var cartesians = [ Cartesian3.fromRadians(0.0, 0.0, 100000.0), Cartesian3.fromRadians(0.0001, 0.0001, 100000.0), Cartesian3.fromRadians(0.0002, 0.0002, 100000.0) ]; var clonedCartesians = [ Cartesian3.fromRadians(0.0, 0.0, 100000.0), Cartesian3.fromRadians(0.0001, 0.0001, 100000.0), Cartesian3.fromRadians(0.0002, 0.0002, 100000.0) ]; return createGlobe().then(function() { return clampToHeightMostDetailed(cartesians).then(function(updatedCartesians) { expect(updatedCartesians).toBe(cartesians); expect(updatedCartesians.length).toBe(3); var previousCartesian; for (var i = 0; i < 3; ++i) { expect(updatedCartesians[i]).not.toEqual(clonedCartesians[i]); expect(updatedCartesians[i]).not.toEqual(previousCartesian); previousCartesian = updatedCartesians[i]; } }); }); }); it('does not clamp to offscreen globe tiles', function() { if (!scene.clampToHeightSupported) { return; } var cartesians = [Cartesian3.fromRadians(0.0, 0.0, 100000.0)]; scene.camera.setView({ destination : offscreenRectangle }); return createGlobe().then(function() { return clampToHeightMostDetailed(cartesians).then(function(updatedCartesians) { expect(updatedCartesians[0]).toBeUndefined(); }); }); }); it('clamps to multiple primitives', function() { if (!scene.clampToHeightSupported) { return; } createRectangle(0.0, smallRectangle); createRectangle(0.0, offscreenRectangle); var cartesians = [ Cartographic.toCartesian(Rectangle.center(smallRectangle)), Cartographic.toCartesian(Rectangle.center(offscreenRectangle)), Cartesian3.fromRadians(-2.0, -2.0) ]; scene.camera.setView({ destination : offscreenRectangle }); return clampToHeightMostDetailed(cartesians).then(function(updatedCartesians) { expect(updatedCartesians[0]).toBeDefined(); expect(updatedCartesians[1]).toBeDefined(); expect(updatedCartesians[2]).toBeUndefined(); // No primitive occupies this space }); }); it('clamps to primitive', function() { if (!scene.clampToHeightSupported) { return; } createSmallRectangle(0.0); var cartesians = [ Cartesian3.fromRadians(0.0, 0.0, 100000.0), Cartesian3.fromRadians(-0.000001, -0.000001, 100000.0), Cartesian3.fromRadians(0.0000005, 0.0000005, 100000.0) ]; var expectedCartesians = [ Cartesian3.fromRadians(0.0, 0.0, 0.0), Cartesian3.fromRadians(-0.000001, -0.000001, 0.0), Cartesian3.fromRadians(0.0000005, 0.0000005, 0.0) ]; scene.camera.setView({ destination : offscreenRectangle }); return clampToHeightMostDetailed(cartesians).then(function(updatedCartesians) { var previousCartesian; for (var i = 0; i < 3; ++i) { expect(updatedCartesians[i]).toEqualEpsilon(expectedCartesians[i], CesiumMath.EPSILON5); expect(updatedCartesians[i]).not.toEqual(previousCartesian); previousCartesian = updatedCartesians[i]; } }); }); it('clamps to top primitive', function() { if (!scene.clampToHeightSupported) { return; } createSmallRectangle(0.0); createSmallRectangle(1.0); var cartesians = [Cartesian3.fromRadians(0.0, 0.0)]; scene.camera.setView({ destination : offscreenRectangle }); return clampToHeightMostDetailed(cartesians).then(function(updatedCartesians) { var expectedCartesian = Cartesian3.fromRadians(0.0, 0.0, 1.0); expect(updatedCartesians[0]).toEqualEpsilon(expectedCartesian, CesiumMath.EPSILON5); }); }); it('excludes objects', function() { if (!scene.clampToHeightSupported) { return; } var rectangle1 = createRectangle(0.0, smallRectangle); createRectangle(0.0, offscreenRectangle); var rectangle3 = createRectangle(1.0, offscreenRectangle); var cartesians = [ Cartographic.toCartesian(Rectangle.center(smallRectangle)), Cartographic.toCartesian(Rectangle.center(offscreenRectangle)), Cartesian3.fromRadians(-2.0, -2.0) ]; scene.camera.setView({ destination : offscreenRectangle }); return clampToHeightMostDetailed(cartesians, [rectangle1, rectangle3]).then(function(updatedCartesians) { var expectedCartesian = Cartographic.toCartesian(Rectangle.center(offscreenRectangle)); expect(updatedCartesians[0]).toBeUndefined(); // This rectangle was excluded expect(updatedCartesians[1]).toEqualEpsilon(expectedCartesian, CesiumMath.EPSILON2); expect(updatedCartesians[2]).toBeUndefined(); // No primitive occupies this space }); }); it('excludes primitive that doesn\'t write depth', function() { if (!scene.clampToHeightSupported) { return; } var rectangle = createSmallRectangle(0.0); var height = 100.0; var cartesian = Cartesian3.fromRadians(0.0, 0.0, height); var cartesians1 = [Cartesian3.clone(cartesian)]; var cartesians2 = [Cartesian3.clone(cartesian)]; var cartesians3 = [Cartesian3.clone(cartesian)]; var collection = scene.primitives.add(new PointPrimitiveCollection()); var point = collection.add({ position : cartesian }); scene.camera.setView({ destination : offscreenRectangle }); return clampToHeightMostDetailed(cartesians1).then(function(updatedCartesians) { expect(updatedCartesians[0]).toEqualEpsilon(cartesian, CesiumMath.EPSILON3); }).then(function() { point.disableDepthTestDistance = Number.POSITIVE_INFINITY; return clampToHeightMostDetailed(cartesians2).then(function(updatedCartesians) { expect(updatedCartesians[0]).toEqualEpsilon(cartesian, CesiumMath.EPSILON3); }); }).then(function() { rectangle.show = false; return clampToHeightMostDetailed(cartesians3).then(function(updatedCartesians) { expect(updatedCartesians[0]).toBeUndefined(); }); }); }); it('handles empty array', function() { if (!scene.clampToHeightSupported) { return; } var cartesians = []; return sampleHeightMostDetailed(cartesians).then(function(updatedCartesians) { expect(updatedCartesians.length).toBe(0); }); }); it('throws if cartesians is undefined', function() { if (!scene.clampToHeightSupported) { return; } expect(function() { scene.clampToHeightMostDetailed(undefined); }).toThrowDeveloperError(); }); it('throws if scene camera is in 2D', function() { if (!scene.clampToHeightSupported) { return; } scene.morphTo2D(0.0); var cartesians = [Cartesian3.fromRadians(0.0, 0.0)]; expect(function() { scene.clampToHeightMostDetailed(cartesians); }).toThrowDeveloperError(); }); it('throws if scene camera is in CV', function() { if (!scene.clampToHeightSupported) { return; } scene.morphToColumbusView(0.0); var cartesians = [Cartesian3.fromRadians(0.0, 0.0)]; expect(function() { scene.clampToHeightMostDetailed(cartesians); }).toThrowDeveloperError(); }); it('throws if clampToHeight is not supported', function() { if (!scene.clampToHeightSupported) { return; } // Disable extension var depthTexture = scene.context._depthTexture; scene.context._depthTexture = false; var cartesians = [Cartesian3.fromRadians(0.0, 0.0)]; expect(function() { scene.clampToHeightMostDetailed(cartesians); }).toThrowDeveloperError(); // Re-enable extension scene.context._depthTexture = depthTexture; }); }); it('calls multiple picking functions within the same frame', function() { if (!scene.clampToHeightSupported || !scene.pickPositionSupported) { return; } createSmallRectangle(0.0); var offscreenRectanglePrimitive = createRectangle(0.0, offscreenRectangle); offscreenRectanglePrimitive.appearance.material.uniforms.color = new Color(1.0, 0.0, 0.0, 1.0); scene.camera.setView({ destination : offscreenRectangle }); // Call render. Lays down depth for the pickPosition call scene.renderForSpecs(); var cartographic = Cartographic.fromRadians(0.0, 0.0, 100000.0); var cartesian = Cartographic.toCartesian(cartographic); var cartesians = [Cartesian3.clone(cartesian)]; var cartographics = [cartographic]; // Call clampToHeight expect(scene).toClampToHeightAndCall(function(cartesian) { var expectedCartesian = Cartesian3.fromRadians(0.0, 0.0); expect(cartesian).toEqualEpsilon(expectedCartesian, CesiumMath.EPSILON5); }, cartesian); // Call pickPosition expect(scene).toPickPositionAndCall(function(cartesian) { var expectedCartesian = Cartographic.toCartesian(Rectangle.center(offscreenRectangle)); expect(cartesian).toEqualEpsilon(expectedCartesian, CesiumMath.EPSILON5); }); // Call clampToHeight again expect(scene).toClampToHeightAndCall(function(cartesian) { var expectedCartesian = Cartesian3.fromRadians(0.0, 0.0); expect(cartesian).toEqualEpsilon(expectedCartesian, CesiumMath.EPSILON5); }, cartesian); // Call pick expect(scene).toPickPrimitive(offscreenRectanglePrimitive); // Call clampToHeight again expect(scene).toClampToHeightAndCall(function(cartesian) { var expectedCartesian = Cartesian3.fromRadians(0.0, 0.0); expect(cartesian).toEqualEpsilon(expectedCartesian, CesiumMath.EPSILON5); }, cartesian); // Call pickPosition on translucent primitive and returns undefined offscreenRectanglePrimitive.appearance.material.uniforms.color = new Color(1.0, 0.0, 0.0, 0.5); scene.renderForSpecs(); expect(scene).toPickPositionAndCall(function(cartesian) { expect(cartesian).toBeUndefined(); }); // Call clampToHeight again expect(scene).toClampToHeightAndCall(function(cartesian) { var expectedCartesian = Cartesian3.fromRadians(0.0, 0.0); expect(cartesian).toEqualEpsilon(expectedCartesian, CesiumMath.EPSILON5); }, cartesian); // Call pickPosition on translucent primitive with pickTranslucentDepth scene.pickTranslucentDepth = true; scene.renderForSpecs(); expect(scene).toPickPositionAndCall(function(cartesian) { var expectedCartesian = Cartographic.toCartesian(Rectangle.center(offscreenRectangle)); expect(cartesian).toEqualEpsilon(expectedCartesian, CesiumMath.EPSILON5); }); // Mix async and sync requests var results = []; var completed = 0; scene.clampToHeightMostDetailed(cartesians).then(function(updatedCartesians) { results.push(updatedCartesians); completed++; }); scene.sampleHeightMostDetailed(cartographics).then(function(updatedCartographics) { results.push(updatedCartographics); completed++; }); // Call clampToHeight again expect(scene).toClampToHeightAndCall(function(cartesian) { var expectedCartesian = Cartesian3.fromRadians(0.0, 0.0); expect(cartesian).toEqualEpsilon(expectedCartesian, CesiumMath.EPSILON5); }, cartesian); return pollToPromise(function() { // Scene requires manual updates in the tests to move along the promise scene.render(); return completed === 2; }).then(function() { expect(results[0][0]).toBeDefined(); expect(results[1][0].height).toBeDefined(); }); }); }, 'WebGL');