defineSuite([ 'Scene/BillboardCollection', 'Core/BoundingRectangle', 'Core/BoundingSphere', 'Core/Cartesian2', 'Core/Cartesian3', 'Core/CesiumTerrainProvider', 'Core/Color', 'Core/DistanceDisplayCondition', 'Core/Math', 'Core/NearFarScalar', 'Core/OrthographicOffCenterFrustum', 'Core/PerspectiveFrustum', 'Core/Rectangle', 'Core/Resource', 'Scene/Billboard', 'Scene/BlendOption', 'Scene/HeightReference', 'Scene/HorizontalOrigin', 'Scene/TextureAtlas', 'Scene/VerticalOrigin', 'Specs/createGlobe', 'Specs/createScene', 'Specs/pollToPromise', 'ThirdParty/when' ], function( BillboardCollection, BoundingRectangle, BoundingSphere, Cartesian2, Cartesian3, CesiumTerrainProvider, Color, DistanceDisplayCondition, CesiumMath, NearFarScalar, OrthographicOffCenterFrustum, PerspectiveFrustum, Rectangle, Resource, Billboard, BlendOption, HeightReference, HorizontalOrigin, TextureAtlas, VerticalOrigin, createGlobe, createScene, pollToPromise, when) { 'use strict'; var scene; var context; var camera; var billboards; var greenImage; var blueImage; var whiteImage; var largeBlueImage; beforeAll(function() { scene = createScene(); context = scene.context; camera = scene.camera; return when.join( Resource.fetchImage('./Data/Images/Green2x2.png').then(function(result) { greenImage = result; }), Resource.fetchImage('./Data/Images/Blue2x2.png').then(function(result) { blueImage = result; }), Resource.fetchImage('./Data/Images/White2x2.png').then(function(result) { whiteImage = result; }), Resource.fetchImage('./Data/Images/Blue10x10.png').then(function(result) { largeBlueImage = result; })); }); afterAll(function() { scene.destroyForSpecs(); }); beforeEach(function() { scene.morphTo3D(0); camera.position = new Cartesian3(10.0, 0.0, 0.0); camera.direction = Cartesian3.negate(Cartesian3.UNIT_X, new Cartesian3()); camera.up = Cartesian3.clone(Cartesian3.UNIT_Z); camera.frustum = new PerspectiveFrustum(); camera.frustum.aspectRatio = scene.drawingBufferWidth / scene.drawingBufferHeight; camera.frustum.fov = CesiumMath.toRadians(60.0); billboards = new BillboardCollection(); scene.primitives.add(billboards); }); afterEach(function() { // billboards are destroyed by removeAll(). scene.primitives.removeAll(); }); it('constructs a default billboard', function() { var b = billboards.add(); expect(b.show).toEqual(true); expect(b.position).toEqual(Cartesian3.ZERO); expect(b.pixelOffset).toEqual(Cartesian2.ZERO); expect(b.eyeOffset).toEqual(Cartesian3.ZERO); expect(b.horizontalOrigin).toEqual(HorizontalOrigin.CENTER); expect(b.verticalOrigin).toEqual(VerticalOrigin.CENTER); expect(b.scale).toEqual(1.0); expect(b.image).toBeUndefined(); expect(b.color.red).toEqual(1.0); expect(b.color.green).toEqual(1.0); expect(b.color.blue).toEqual(1.0); expect(b.color.alpha).toEqual(1.0); expect(b.rotation).toEqual(0.0); expect(b.alignedAxis).toEqual(Cartesian3.ZERO); expect(b.scaleByDistance).toBeUndefined(); expect(b.translucencyByDistance).toBeUndefined(); expect(b.pixelOffsetScaleByDistance).toBeUndefined(); expect(b.width).toBeUndefined(); expect(b.height).toBeUndefined(); expect(b.id).toBeUndefined(); expect(b.heightReference).toEqual(HeightReference.NONE); expect(b.sizeInMeters).toEqual(false); expect(b.distanceDisplayCondition).toBeUndefined(); expect(b.disableDepthTestDistance).toBeUndefined(); }); it('can add and remove before first update.', function() { var b = billboards.add(); billboards.remove(b); scene.renderForSpecs(); }); it('explicitly constructs a billboard', function() { var b = billboards.add({ show : false, position : new Cartesian3(1.0, 2.0, 3.0), pixelOffset : new Cartesian2(1.0, 2.0), eyeOffset : new Cartesian3(1.0, 2.0, 3.0), horizontalOrigin : HorizontalOrigin.LEFT, verticalOrigin : VerticalOrigin.BOTTOM, scale : 2.0, image : greenImage, color : { red : 1.0, green : 2.0, blue : 3.0, alpha : 4.0 }, rotation : 1.0, alignedAxis : Cartesian3.UNIT_Z, scaleByDistance : new NearFarScalar(1.0, 3.0, 1.0e6, 0.0), translucencyByDistance : new NearFarScalar(1.0, 1.0, 1.0e6, 0.0), pixelOffsetScaleByDistance : new NearFarScalar(1.0, 1.0, 1.0e6, 0.0), width : 300.0, height : 200.0, sizeInMeters : true, distanceDisplayCondition : new DistanceDisplayCondition(10.0, 100.0), disableDepthTestDistance : 10.0, id : 'id' }); expect(b.show).toEqual(false); expect(b.position).toEqual(new Cartesian3(1.0, 2.0, 3.0)); expect(b.pixelOffset).toEqual(new Cartesian2(1.0, 2.0)); expect(b.eyeOffset).toEqual(new Cartesian3(1.0, 2.0, 3.0)); expect(b.horizontalOrigin).toEqual(HorizontalOrigin.LEFT); expect(b.verticalOrigin).toEqual(VerticalOrigin.BOTTOM); expect(b.scale).toEqual(2.0); expect(b.image).toEqual(greenImage.src); expect(b.color.red).toEqual(1.0); expect(b.color.green).toEqual(2.0); expect(b.color.blue).toEqual(3.0); expect(b.color.alpha).toEqual(4.0); expect(b.rotation).toEqual(1.0); expect(b.alignedAxis).toEqual(Cartesian3.UNIT_Z); expect(b.scaleByDistance).toEqual(new NearFarScalar(1.0, 3.0, 1.0e6, 0.0)); expect(b.translucencyByDistance).toEqual(new NearFarScalar(1.0, 1.0, 1.0e6, 0.0)); expect(b.pixelOffsetScaleByDistance).toEqual(new NearFarScalar(1.0, 1.0, 1.0e6, 0.0)); expect(b.width).toEqual(300.0); expect(b.height).toEqual(200.0); expect(b.sizeInMeters).toEqual(true); expect(b.distanceDisplayCondition).toEqual(new DistanceDisplayCondition(10.0, 100.0)); expect(b.disableDepthTestDistance).toEqual(10.0); expect(b.id).toEqual('id'); }); it('sets billboard properties', function() { var b = billboards.add(); b.show = false; b.position = new Cartesian3(1.0, 2.0, 3.0); b.pixelOffset = new Cartesian2(1.0, 2.0); b.eyeOffset = new Cartesian3(1.0, 2.0, 3.0); b.horizontalOrigin = HorizontalOrigin.LEFT; b.verticalOrigin = VerticalOrigin.BOTTOM; b.scale = 2.0; b.image = greenImage; b.color = new Color(1.0, 2.0, 3.0, 4.0); b.rotation = 1.0; b.alignedAxis = Cartesian3.UNIT_Z; b.width = 300.0; b.height = 200.0; b.scaleByDistance = new NearFarScalar(1.0e6, 3.0, 1.0e8, 0.0); b.translucencyByDistance = new NearFarScalar(1.0e6, 1.0, 1.0e8, 0.0); b.pixelOffsetScaleByDistance = new NearFarScalar(1.0e6, 3.0, 1.0e8, 0.0); b.sizeInMeters = true; b.distanceDisplayCondition = new DistanceDisplayCondition(10.0, 100.0); b.disableDepthTestDistance = 10.0; expect(b.show).toEqual(false); expect(b.position).toEqual(new Cartesian3(1.0, 2.0, 3.0)); expect(b.pixelOffset).toEqual(new Cartesian2(1.0, 2.0)); expect(b.eyeOffset).toEqual(new Cartesian3(1.0, 2.0, 3.0)); expect(b.horizontalOrigin).toEqual(HorizontalOrigin.LEFT); expect(b.verticalOrigin).toEqual(VerticalOrigin.BOTTOM); expect(b.scale).toEqual(2.0); expect(b.image).toEqual(greenImage.src); expect(b.color.red).toEqual(1.0); expect(b.color.green).toEqual(2.0); expect(b.color.blue).toEqual(3.0); expect(b.color.alpha).toEqual(4.0); expect(b.rotation).toEqual(1.0); expect(b.alignedAxis).toEqual(Cartesian3.UNIT_Z); expect(b.scaleByDistance).toEqual(new NearFarScalar(1.0e6, 3.0, 1.0e8, 0.0)); expect(b.translucencyByDistance).toEqual(new NearFarScalar(1.0e6, 1.0, 1.0e8, 0.0)); expect(b.pixelOffsetScaleByDistance).toEqual(new NearFarScalar(1.0e6, 3.0, 1.0e8, 0.0)); expect(b.width).toEqual(300.0); expect(b.height).toEqual(200.0); expect(b.sizeInMeters).toEqual(true); expect(b.distanceDisplayCondition).toEqual(new DistanceDisplayCondition(10.0, 100.0)); expect(b.disableDepthTestDistance).toEqual(10.0); }); it('is not destroyed', function() { expect(billboards.isDestroyed()).toEqual(false); }); it('renders billboard in multiple passes', function() { billboards.add({ position : Cartesian3.ZERO, image : greenImage }); camera.position = new Cartesian3(2.0, 0.0, 0.0); var frameState = scene.frameState; frameState.commandList.length = 0; billboards.blendOption = BlendOption.OPAQUE_AND_TRANSLUCENT; billboards.update(frameState); expect(frameState.commandList.length).toEqual(2); frameState.commandList.length = 0; billboards.blendOption = BlendOption.OPAQUE; billboards.update(frameState); expect(frameState.commandList.length).toEqual(1); frameState.commandList.length = 0; billboards.blendOption = BlendOption.TRANSLUCENT; billboards.update(frameState); expect(frameState.commandList.length).toEqual(1); }); it('renders billboard with sizeInMeters', function() { billboards.add({ position : Cartesian3.ZERO, image : greenImage, width : 2.0, height : 2.0, sizeInMeters : true }); camera.position = new Cartesian3(2.0, 0.0, 0.0); expect(scene).toRender([0, 255, 0, 255]); camera.position = new Cartesian3(1e6, 0.0, 0.0); expect(scene).toRender([0, 0, 0, 255]); }); it('disables billboard scaleByDistance', function() { var b = billboards.add({ scaleByDistance : new NearFarScalar(1.0, 3.0, 1.0e6, 0.0) }); b.scaleByDistance = undefined; expect(b.scaleByDistance).toBeUndefined(); }); it('disables billboard translucencyByDistance', function() { var b = billboards.add({ translucencyByDistance : new NearFarScalar(1.0, 1.0, 1.0e6, 0.0) }); b.translucencyByDistance = undefined; expect(b.translucencyByDistance).toBeUndefined(); }); it('disables billboard pixelOffsetScaleByDistance', function() { var b = billboards.add({ pixelOffsetScaleByDistance : new NearFarScalar(1.0, 1.0, 1.0e6, 0.0) }); b.pixelOffsetScaleByDistance = undefined; expect(b.pixelOffsetScaleByDistance).toBeUndefined(); }); it('renders billboard with scaleByDistance', function() { billboards.add({ position : Cartesian3.ZERO, scaleByDistance: new NearFarScalar(2.0, 1.0, 4.0, 0.0), image : greenImage }); camera.position = new Cartesian3(2.0, 0.0, 0.0); expect(scene).toRender([0, 255, 0, 255]); camera.position = new Cartesian3(4.0, 0.0, 0.0); expect(scene).toRender([0, 0, 0, 255]); }); it('renders billboard with translucencyByDistance', function() { billboards.add({ position : Cartesian3.ZERO, translucencyByDistance: new NearFarScalar(2.0, 1.0, 4.0, 0.0), image : greenImage }); camera.position = new Cartesian3(2.0, 0.0, 0.0); expect(scene).toRender([0, 255, 0, 255]); camera.position = new Cartesian3(4.0, 0.0, 0.0); expect(scene).toRender([0, 0, 0, 255]); }); it('renders billboard with pixelOffsetScaleByDistance', function() { billboards.add({ position : Cartesian3.ZERO, pixelOffset : new Cartesian2(1.0, 0.0), pixelOffsetScaleByDistance: new NearFarScalar(2.0, 0.0, 4.0, 1000.0), image : greenImage }); camera.position = new Cartesian3(2.0, 0.0, 0.0); expect(scene).toRender([0, 255, 0, 255]); camera.position = new Cartesian3(4.0, 0.0, 0.0); expect(scene).toRender([0, 0, 0, 255]); }); it('throws scaleByDistance with nearDistance === farDistance', function() { var b = billboards.add(); var scale = new NearFarScalar(2.0e5, 1.0, 2.0e5, 0.0); expect(function() { b.scaleByDistance = scale; }).toThrowDeveloperError(); }); it('throws new billboard with invalid scaleByDistance (nearDistance === farDistance)', function() { var scale = new NearFarScalar(2.0e5, 1.0, 2.0e5, 0.0); expect(function() { billboards.add({ scaleByDistance : scale }); }).toThrowDeveloperError(); }); it('throws scaleByDistance with nearDistance > farDistance', function() { var b = billboards.add(); var scale = new NearFarScalar(1.0e9, 1.0, 1.0e5, 1.0); expect(function() { b.scaleByDistance = scale; }).toThrowDeveloperError(); }); it('throws pixelOffsetScaleByDistance with nearDistance === farDistance', function() { var b = billboards.add(); var scale = new NearFarScalar(2.0e5, 1.0, 2.0e5, 0.0); expect(function() { b.pixelOffsetScaleByDistance = scale; }).toThrowDeveloperError(); }); it('throws new billboard with invalid pixelOffsetScaleByDistance (nearDistance === farDistance)', function() { var scale = new NearFarScalar(2.0e5, 1.0, 2.0e5, 0.0); expect(function() { billboards.add({ pixelOffsetScaleByDistance : scale }); }).toThrowDeveloperError(); }); it('throws pixelOffsetScaleByDistance with nearDistance > farDistance', function() { var b = billboards.add(); var scale = new NearFarScalar(1.0e9, 1.0, 1.0e5, 1.0); expect(function() { b.pixelOffsetScaleByDistance = scale; }).toThrowDeveloperError(); }); it('throws translucencyByDistance with nearDistance === farDistance', function() { var b = billboards.add(); var translucency = new NearFarScalar(2.0e5, 1.0, 2.0e5, 0.0); expect(function() { b.translucencyByDistance = translucency; }).toThrowDeveloperError(); }); it('throws new billboard with invalid translucencyByDistance (nearDistance === farDistance)', function() { var translucency = new NearFarScalar(2.0e5, 1.0, 2.0e5, 0.0); expect(function() { billboards.add({ translucencyByDistance : translucency }); }).toThrowDeveloperError(); }); it('throws translucencyByDistance with nearDistance > farDistance', function() { var b = billboards.add(); var translucency = new NearFarScalar(1.0e9, 1.0, 1.0e5, 1.0); expect(function() { b.translucencyByDistance = translucency; }).toThrowDeveloperError(); }); it('renders billboard with distanceDisplayCondition', function() { billboards.add({ position : Cartesian3.ZERO, image : greenImage, distanceDisplayCondition : new DistanceDisplayCondition(10.0, 100.0) }); camera.position = new Cartesian3(200.0, 0.0, 0.0); expect(scene).toRender([0, 0, 0, 255]); camera.position = new Cartesian3(50.0, 0.0, 0.0); expect(scene).toRender([0, 255, 0, 255]); camera.position = new Cartesian3(5.0, 0.0, 0.0); expect(scene).toRender([0, 0, 0, 255]); }); it('throws new billboard with invalid distanceDisplayCondition (near >= far)', function() { var dc = new DistanceDisplayCondition(100.0, 10.0); expect(function() { billboards.add({ distanceDisplayCondition : dc }); }).toThrowDeveloperError(); }); it('throws distanceDisplayCondition with near >= far', function() { var b = billboards.add(); var dc = new DistanceDisplayCondition(100.0, 10.0); expect(function() { b.distanceDisplayCondition = dc; }).toThrowDeveloperError(); }); it('renders with disableDepthTestDistance', function() { var b = billboards.add({ position : new Cartesian3(-1.0, 0.0, 0.0), image : greenImage }); billboards.add({ position : Cartesian3.ZERO, image : blueImage }); expect(scene).toRender([0, 0, 255, 255]); b.disableDepthTestDistance = Number.POSITIVE_INFINITY; expect(scene).toRender([0, 255, 0, 255]); }); it('throws with new billboard with disableDepthTestDistance less than 0.0', function() { expect(function() { billboards.add({ disableDepthTestDistance : -1.0 }); }).toThrowDeveloperError(); }); it('throws with disableDepthTestDistance set less than 0.0', function() { var b = billboards.add(); expect(function() { b.disableDepthTestDistance = -1.0; }).toThrowDeveloperError(); }); it('sets a removed billboard property', function() { var b = billboards.add(); billboards.remove(b); b.show = false; expect(b.show).toEqual(false); }); it('has zero billboards when constructed', function() { expect(billboards.length).toEqual(0); }); it('adds a billboard', function() { var b = billboards.add({ position : new Cartesian3(1.0, 2.0, 3.0) }); expect(billboards.length).toEqual(1); expect(billboards.get(0)).toEqual(b); }); it('removes the first billboard', function() { var one = billboards.add({ position : new Cartesian3(1.0, 2.0, 3.0) }); var two = billboards.add({ position : new Cartesian3(4.0, 5.0, 6.0) }); expect(billboards.length).toEqual(2); expect(billboards.remove(one)).toEqual(true); expect(billboards.length).toEqual(1); expect(billboards.get(0)).toEqual(two); }); it('removes the last billboard', function() { var one = billboards.add({ position : new Cartesian3(1.0, 2.0, 3.0) }); var two = billboards.add({ position : new Cartesian3(4.0, 5.0, 6.0) }); expect(billboards.length).toEqual(2); expect(billboards.remove(two)).toEqual(true); expect(billboards.length).toEqual(1); expect(billboards.get(0)).toEqual(one); }); it('removes the same billboard twice', function() { var b = billboards.add({ position : new Cartesian3(1.0, 2.0, 3.0) }); expect(billboards.length).toEqual(1); expect(billboards.remove(b)).toEqual(true); expect(billboards.length).toEqual(0); expect(billboards.remove(b)).toEqual(false); expect(billboards.length).toEqual(0); }); it('returns false when removing undefined', function() { billboards.add({ position : new Cartesian3(1.0, 2.0, 3.0) }); expect(billboards.length).toEqual(1); expect(billboards.remove(undefined)).toEqual(false); expect(billboards.length).toEqual(1); }); it('adds and removes billboards', function() { var one = billboards.add({ position : new Cartesian3(1.0, 2.0, 3.0) }); var two = billboards.add({ position : new Cartesian3(4.0, 5.0, 6.0) }); expect(billboards.length).toEqual(2); expect(billboards.get(0)).toEqual(one); expect(billboards.get(1)).toEqual(two); expect(billboards.remove(two)).toEqual(true); var three = billboards.add({ position : new Cartesian3(7.0, 8.0, 9.0) }); expect(billboards.length).toEqual(2); expect(billboards.get(0)).toEqual(one); expect(billboards.get(1)).toEqual(three); }); it('removes all billboards', function() { billboards.add({ position : new Cartesian3(1.0, 2.0, 3.0) }); billboards.add({ position : new Cartesian3(4.0, 5.0, 6.0) }); expect(billboards.length).toEqual(2); billboards.removeAll(); expect(billboards.length).toEqual(0); }); it('can check if it contains a billboard', function() { var billboard = billboards.add(); expect(billboards.contains(billboard)).toEqual(true); }); it('returns false when checking if it contains a billboard it does not contain', function() { var billboard = billboards.add(); billboards.remove(billboard); expect(billboards.contains(billboard)).toEqual(false); }); it('does not contain undefined', function() { expect(billboards.contains(undefined)).toEqual(false); }); it('does not contain random other objects', function() { expect(billboards.contains({})).toEqual(false); expect(billboards.contains(new Cartesian2())).toEqual(false); }); it('sets and gets a texture atlas', function() { expect(billboards.textureAtlas).toBeUndefined(); var atlas = new TextureAtlas({ context : scene.context }); billboards.textureAtlas = atlas; expect(billboards.textureAtlas).toEqual(atlas); }); it('destroys a texture atlas', function() { var b = new BillboardCollection(); expect(b.destroyTextureAtlas).toEqual(true); var atlas = new TextureAtlas({ context : scene.context }); b.textureAtlas = atlas; b = b.destroy(); expect(atlas.isDestroyed()).toEqual(true); }); it('does not destroy a texture atlas', function() { var b = new BillboardCollection(); b.destroyTextureAtlas = false; var atlas = new TextureAtlas({ context : scene.context }); b.rextureAtlas = atlas; b = b.destroy(); expect(atlas.isDestroyed()).toEqual(false); }); it('does not render when constructed', function() { expect(scene).toRender([0, 0, 0, 255]); }); it('modifies and removes a billboard, then renders', function() { var b1 = billboards.add({ position : Cartesian3.ZERO, image : greenImage }); billboards.add({ position : new Cartesian3(-1.0, 0.0, 0.0), image : largeBlueImage }); expect(scene).toRender([0, 255, 0, 255]); b1.scale = 2.0; billboards.remove(b1); expect(scene).toRender([0, 0, 255, 255]); }); it('renders a green billboard', function() { billboards.add({ position : Cartesian3.ZERO, image : greenImage }); expect(scene).toRender([0, 255, 0, 255]); }); it('adds and renders a billboard', function() { billboards.add({ position : Cartesian3.ZERO, image : greenImage }); expect(scene).toRender([0, 255, 0, 255]); billboards.add({ position : new Cartesian3(1.0, 0.0, 0.0), // Closer to camera image : largeBlueImage }); expect(scene).toRender([0, 0, 255, 255]); }); it('removes and renders a billboard', function() { billboards.add({ position : Cartesian3.ZERO, image : greenImage }); var blueBillboard = billboards.add({ position : new Cartesian3(1.0, 0.0, 0.0), // Closer to camera image : largeBlueImage }); expect(scene).toRender([0, 0, 255, 255]); billboards.remove(blueBillboard); expect(scene).toRender([0, 255, 0, 255]); }); it('removes all billboards and renders', function() { billboards.add({ position : Cartesian3.ZERO, image : greenImage }); expect(scene).toRender([0, 255, 0, 255]); billboards.removeAll(); expect(scene).toRender([0, 0, 0, 255]); }); it('removes all billboards, adds a billboard, and renders', function() { billboards.add({ position : Cartesian3.ZERO, image : greenImage }); expect(scene).toRender([0, 255, 0, 255]); billboards.removeAll(); billboards.add({ position : Cartesian3.ZERO, image : largeBlueImage }); expect(scene).toRender([0, 0, 255, 255]); }); it('renders with a different texture atlas', function() { var b = billboards.add({ position : Cartesian3.ZERO, image : greenImage }); expect(scene).toRender([0, 255, 0, 255]); billboards.textureAtlas = new TextureAtlas({ context : scene.context }); b.image = blueImage; expect(scene).toRender([0, 0, 255, 255]); }); it('renders using billboard show property', function() { var greenBillboard = billboards.add({ position : Cartesian3.ZERO, image : greenImage }); var blueBillboard = billboards.add({ show : false, position : Cartesian3.ZERO, image : largeBlueImage }); expect(scene).toRender([0, 255, 0, 255]); greenBillboard.show = false; blueBillboard.show = true; expect(scene).toRender([0, 0, 255, 255]); }); it('renders using billboard position property', function() { var b = billboards.add({ position : Cartesian3.ZERO, image : greenImage }); expect(scene).toRender([0, 255, 0, 255]); b.position = new Cartesian3(20.0, 0.0, 0.0); // Behind camera expect(scene).toRender([0, 0, 0, 255]); b.position = new Cartesian3(1.0, 0.0, 0.0); // Back in front of camera expect(scene).toRender([0, 255, 0, 255]); }); it('renders using billboard scale property', function() { var b = billboards.add({ position : Cartesian3.ZERO, image : greenImage }); expect(scene).toRender([0, 255, 0, 255]); b.scale = 0.0; expect(scene).toRender([0, 0, 0, 255]); b.scale = 2.0; expect(scene).toRender([0, 255, 0, 255]); }); it('renders using billboard image property', function() { var b = billboards.add({ position : Cartesian3.ZERO, image : greenImage }); expect(scene).toRender([0, 255, 0, 255]); b.image = largeBlueImage; expect(scene).toRender([0, 0, 255, 255]); }); it('renders using billboard setImage function', function() { var b = billboards.add({ position : Cartesian3.ZERO, image : greenImage }); expect(scene).toRender([0, 255, 0, 255]); b.setImage(largeBlueImage.src, largeBlueImage); expect(scene).toRender([0, 0, 255, 255]); }); it('renders using billboard setImageSubRegion function', function() { var b = billboards.add({ position : Cartesian3.ZERO, image : greenImage }); expect(scene).toRender([0, 255, 0, 255]); billboards.textureAtlas.addImage(largeBlueImage.src, largeBlueImage); b.setImageSubRegion(largeBlueImage.src, new BoundingRectangle(5.0, 5.0, 1.0, 1.0)); expect(scene).toRender([0, 0, 255, 255]); }); it('renders using billboard color property', function() { var b = billboards.add({ position : Cartesian3.ZERO, image : whiteImage }); expect(scene).toRender([255, 255, 255, 255]); b.color = new Color(1.0, 0.0, 1.0, 1.0); expect(scene).toRender([255, 0, 255, 255]); // Update a second time since it goes through a different vertex array update path b.color = new Color(0.0, 1.0, 0.0, 1.0); expect(scene).toRender([0, 255, 0, 255]); }); it('renders using billboard rotation property', function() { var b = billboards.add({ position : Cartesian3.ZERO, image : greenImage }); b.rotation = CesiumMath.PI_OVER_TWO; expect(scene).toRender([0, 255, 0, 255]); }); it('renders using billboard aligned axis property', function() { var b = billboards.add({ position : Cartesian3.ZERO, image : greenImage }); b.alignedAxis = Cartesian3.UNIT_X; expect(scene).toRender([0, 255, 0, 255]); }); it('renders using billboard custom width property', function() { var b = billboards.add({ position : Cartesian3.ZERO, image : greenImage }); b.width = 300.0; expect(scene).toRender([0, 255, 0, 255]); }); it('renders using billboard custom height property', function() { var b = billboards.add({ position : Cartesian3.ZERO, image : greenImage }); b.height = 300.0; expect(scene).toRender([0, 255, 0, 255]); }); it('renders bounding volume with debugShowBoundingVolume', function() { billboards.add({ position : Cartesian3.ZERO, image : greenImage, scale : 0.5 // bring bounding volume in view }); billboards.debugShowBoundingVolume = true; expect(scene).notToRender([0, 0, 0, 255]); }); it('renders billboards when instancing is disabled', function() { // disable extension var instancedArrays = context._instancedArrays; context._instancedArrays = undefined; expect(scene).toRender([0, 0, 0, 255]); var b1 = billboards.add({ position : Cartesian3.ZERO, image : greenImage }); expect(scene).toRender([0, 255, 0, 255]); var b2 = billboards.add({ position : new Cartesian3(1.0, 0.0, 0.0), // Closer to camera image : largeBlueImage }); expect(scene).toRender([0, 0, 255, 255]); billboards.remove(b2); expect(scene).toRender([0, 255, 0, 255]); billboards.remove(b1); expect(scene).toRender([0, 0, 0, 255]); context._instancedArrays = instancedArrays; }); it('updates 10% of billboards', function() { for ( var i = 0; i < 10; ++i) { billboards.add({ position : Cartesian3.ZERO, image : whiteImage, show : (i === 3) }); } // First render - default billboard color is white. expect(scene).toRender([255, 255, 255, 255]); billboards.get(3).color = new Color(0.0, 1.0, 0.0, 1.0); // Second render - billboard is green expect(scene).toRender([0, 255, 0, 255]); billboards.get(3).color = new Color(1.0, 0.0, 0.0, 1.0); // Third render - update goes through a different vertex array update path expect(scene).toRender([255, 0, 0, 255]); }); it('renders more than 16K billboards', function() { if(!context.instancedArrays) { return; } for ( var i = 0; i < 16 * 1024; ++i) { billboards.add({ position : Cartesian3.ZERO, image : whiteImage, color : { alpha : 0.0 } }); } billboards.add({ position : Cartesian3.ZERO, image : whiteImage }); expect(scene).toRender([255, 255, 255, 255]); }); it('is picked', function() { var b = billboards.add({ position : Cartesian3.ZERO, image : whiteImage, id : 'id' }); expect(scene).toPickAndCall(function(result) { expect(result.primitive).toEqual(b); expect(result.id).toEqual('id'); }); }); it('can change pick id', function() { var b = billboards.add({ position : Cartesian3.ZERO, image : whiteImage, id : 'id' }); expect(scene).toPickAndCall(function(result) { expect(result.primitive).toEqual(b); expect(result.id).toEqual('id'); }); b.id = 'id2'; expect(scene).toPickAndCall(function(result) { expect(result.primitive).toEqual(b); expect(result.id).toEqual('id2'); }); }); it('is not picked', function() { billboards.add({ show : false, position : Cartesian3.ZERO, image : whiteImage }); expect(scene).notToPick(); }); it('picks a billboard using scaleByDistance', function() { var b = billboards.add({ position : Cartesian3.ZERO, image : whiteImage }); var scaleByDistance = new NearFarScalar(1.0, 4.0, 3.0e9, 2.0); b.scaleByDistance = scaleByDistance; expect(scene).toPickPrimitive(b); scaleByDistance.nearValue = 0.0; scaleByDistance.farValue = 0.0; b.scaleByDistance = scaleByDistance; expect(scene).notToPick(); }); it('picks a billboard using translucencyByDistance', function() { var b = billboards.add({ position : Cartesian3.ZERO, image : whiteImage }); var translucency = new NearFarScalar(1.0, 0.9, 3.0e9, 0.8); b.translucencyByDistance = translucency; expect(scene).toPickPrimitive(b); translucency.nearValue = 0.0; translucency.farValue = 0.0; b.translucencyByDistance = translucency; expect(scene).notToPick(); }); it('picks a billboard using pixelOffsetScaleByDistance', function() { var b = billboards.add({ position : Cartesian3.ZERO, pixelOffset : new Cartesian2(0.0, 100.0), image : whiteImage }); var pixelOffsetScale = new NearFarScalar(1.0, 0.0, 3.0e9, 0.0); b.pixelOffsetScaleByDistance = pixelOffsetScale; expect(scene).toPickPrimitive(b); pixelOffsetScale.nearValue = 10.0; pixelOffsetScale.farValue = 10.0; b.pixelOffsetScaleByDistance = pixelOffsetScale; expect(scene).notToPick(); }); it('can pick a billboard using the rotation property', function() { var b = billboards.add({ position : Cartesian3.ZERO, image : greenImage }); b.rotation = CesiumMath.PI_OVER_TWO; expect(scene).toPickPrimitive(b); }); it('can pick a billboard using the aligned axis property', function() { var b = billboards.add({ position : Cartesian3.ZERO, image : greenImage }); b.alignedAxis = Cartesian3.UNIT_X; expect(scene).toPickPrimitive(b); }); it('computes screen space position', function() { var b = billboards.add({ position : Cartesian3.ZERO }); scene.renderForSpecs(); expect(b.computeScreenSpacePosition(scene)).toEqualEpsilon(new Cartesian2(0.5, 0.5), CesiumMath.EPSILON1); }); it('stores screen space position in a result', function() { var b = billboards.add({ position : Cartesian3.ZERO }); var result = new Cartesian2(); scene.renderForSpecs(); var actual = b.computeScreenSpacePosition(scene, result); expect(actual).toEqual(result); expect(result).toEqualEpsilon(new Cartesian2(0.5, 0.5), CesiumMath.EPSILON1); }); it('computes screen space position with pixelOffset', function() { var b = billboards.add({ position : Cartesian3.ZERO, pixelOffset : new Cartesian2(0.5, 0.5) }); scene.renderForSpecs(); expect(b.computeScreenSpacePosition(scene)).toEqualEpsilon(new Cartesian2(1, 1.0), CesiumMath.EPSILON1); }); it('computes screen space position with eyeOffset', function() { var b = billboards.add({ position : Cartesian3.ZERO, eyeOffset : new Cartesian3(1.0, 1.0, 0.0) }); scene.renderForSpecs(); expect(b.computeScreenSpacePosition(scene)).toEqualEpsilon(new Cartesian2(0.5, 0.5), CesiumMath.EPSILON1); }); it('computes screen space position in Columbus view', function() { var b = billboards.add({ position : Cartesian3.fromDegrees(0.0, 0.0, 10.0) }); scene.morphToColumbusView(0.0); scene.camera.setView({ destination : Rectangle.MAX_VALUE }); scene.renderForSpecs(); expect(b.computeScreenSpacePosition(scene)).toEqualEpsilon(new Cartesian2(0.5, 0.5), CesiumMath.EPSILON1); }); it('computes screen space position in 2D', function() { var b = billboards.add({ position : Cartesian3.fromDegrees(0.0, 0.0, 10.0) }); scene.morphTo2D(0.0); scene.camera.setView({ destination : Rectangle.MAX_VALUE }); scene.renderForSpecs(); expect(b.computeScreenSpacePosition(scene)).toEqualEpsilon(new Cartesian2(0.5, 0.5), CesiumMath.EPSILON1); }); it('throws when computing screen space position when not in a collection', function() { var b = billboards.add({ position : Cartesian3.ZERO }); billboards.remove(b); expect(function() { b.computeScreenSpacePosition(scene); }).toThrowDeveloperError(); }); it('throws when computing screen space position without scene', function() { var b = billboards.add(); expect(function() { b.computeScreenSpacePosition(); }).toThrowDeveloperError(); }); it('computes screen space bounding box', function() { var width = 10; var height = 15; var scale = 1.5; var b = billboards.add({ width : width, height : height, scale : scale }); var halfWidth = width * scale * 0.5; var halfHeight = height * scale * 0.5; width = width * scale; height = height * scale; var bbox = Billboard.getScreenSpaceBoundingBox(b, Cartesian2.ZERO); expect(bbox.x).toEqual(-halfWidth); expect(bbox.y).toEqual(-halfHeight); expect(bbox.width).toEqual(width); expect(bbox.height).toEqual(height); }); it('computes screen space bounding box with result', function() { var width = 10; var height = 15; var scale = 1.5; var b = billboards.add({ width : width, height : height, scale : scale }); var halfWidth = width * scale * 0.5; var halfHeight = height * scale * 0.5; width = width * scale; height = height * scale; var result = new BoundingRectangle(); var bbox = Billboard.getScreenSpaceBoundingBox(b, Cartesian2.ZERO, result); expect(bbox.x).toEqual(-halfWidth); expect(bbox.y).toEqual(-halfHeight); expect(bbox.width).toEqual(width); expect(bbox.height).toEqual(height); expect(bbox).toBe(result); }); it('computes screen space bounding box with vertical origin', function() { var width = 10; var height = 15; var scale = 1.5; var b = billboards.add({ width : width, height : height, scale : scale, verticalOrigin : VerticalOrigin.BOTTOM }); var halfWidth = width * scale * 0.5; width = width * scale; height = height * scale; var bbox = Billboard.getScreenSpaceBoundingBox(b, Cartesian2.ZERO); expect(bbox.x).toEqual(-halfWidth); expect(bbox.y).toEqual(-height); expect(bbox.width).toEqual(width); expect(bbox.height).toEqual(height); b.verticalOrigin = VerticalOrigin.TOP; bbox = Billboard.getScreenSpaceBoundingBox(b, Cartesian2.ZERO); expect(bbox.x).toEqual(-halfWidth); expect(bbox.y).toEqual(0); expect(bbox.width).toEqual(width); expect(bbox.height).toEqual(height); }); it('computes screen space bounding box with horizontal origin', function() { var width = 10; var height = 15; var scale = 1.5; var b = billboards.add({ width : width, height : height, scale : scale, horizontalOrigin : HorizontalOrigin.LEFT }); var halfHeight = height * scale * 0.5; height = height * scale; width = width * scale; var bbox = Billboard.getScreenSpaceBoundingBox(b, Cartesian2.ZERO); expect(bbox.x).toEqual(0); expect(bbox.y).toEqual(-halfHeight); expect(bbox.width).toEqual(width); expect(bbox.height).toEqual(height); b.horizontalOrigin = HorizontalOrigin.RIGHT; bbox = Billboard.getScreenSpaceBoundingBox(b, Cartesian2.ZERO); expect(bbox.x).toEqual(-width); expect(bbox.y).toEqual(-halfHeight); expect(bbox.width).toEqual(width); expect(bbox.height).toEqual(height); }); it('equals another billboard', function() { var b = billboards.add({ position : new Cartesian3(1.0, 2.0, 3.0), color : { red : 1.0, green : 0.0, blue : 0.0, alpha : 1.0 } }); var b2 = billboards.add({ position : new Cartesian3(1.0, 2.0, 3.0), color : { red : 1.0, green : 0.0, blue : 0.0, alpha : 1.0 } }); // This tests the `BillboardCollection.equals` function itself, not simple equality. expect(b.equals(b2)).toEqual(true); }); it('does not equal another billboard', function() { var b = billboards.add({ position : new Cartesian3(1.0, 2.0, 3.0) }); var b2 = billboards.add({ position : new Cartesian3(4.0, 5.0, 6.0) }); // This tests the `BillboardCollection.equals` function itself, not simple equality. expect(b.equals(b2)).toEqual(false); }); it('does not equal undefined', function() { // This tests the `BillboardCollection.equals` function itself, not simple equality. var billboard = billboards.add(); expect(billboard.equals(undefined)).toEqual(false); }); it('throws when accessing without an index', function() { expect(function() { billboards.get(); }).toThrowDeveloperError(); }); it('setImage throws without an id', function() { var b = billboards.add(); expect(function() { b.setImage(undefined, {}); }).toThrowDeveloperError(); }); it('setImage throws without an inmage', function() { var b = billboards.add(); expect(function() { b.setImage('', undefined); }).toThrowDeveloperError(); }); it('setImageSubRegion throws without an id', function() { var b = billboards.add(); expect(function() { b.setImage(undefined, {}); }).toThrowDeveloperError(); }); it('setImageSubRegion throws without a sub-region', function() { var b = billboards.add(); expect(function() { b.setImage('', undefined); }).toThrowDeveloperError(); }); it('computes bounding sphere in 3D', function() { var one = billboards.add({ image : greenImage, position : Cartesian3.fromDegrees(-50.0, -50.0) }); var two = billboards.add({ image : greenImage, position : Cartesian3.fromDegrees(-50.0, 50.0) }); scene.renderForSpecs(); var actual = scene.frameState.commandList[0].boundingVolume; var positions = [one.position, two.position]; var expected = BoundingSphere.fromPoints(positions); expect(actual.center).toEqual(expected.center); expect(actual.radius).toEqual(expected.radius); }); it('computes bounding sphere in Columbus view', function() { var projection = scene.mapProjection; var ellipsoid = projection.ellipsoid; var one = billboards.add({ image : greenImage, position : Cartesian3.fromDegrees(-50.0, -50.0) }); var two = billboards.add({ image : greenImage, position : Cartesian3.fromDegrees(-50.0, 50.0) }); // Update scene state scene.morphToColumbusView(0); scene.renderForSpecs(); var actual = scene.frameState.commandList[0].boundingVolume; var projectedPositions = [ projection.project(ellipsoid.cartesianToCartographic(one.position)), projection.project(ellipsoid.cartesianToCartographic(two.position)) ]; var expected = BoundingSphere.fromPoints(projectedPositions); expected.center = new Cartesian3(0.0, expected.center.x, expected.center.y); expect(actual.center).toEqualEpsilon(expected.center, CesiumMath.EPSILON8); expect(actual.radius).toBeGreaterThanOrEqualTo(expected.radius); }); it('computes bounding sphere in 2D', function() { var projection = scene.mapProjection; var ellipsoid = projection.ellipsoid; var one = billboards.add({ image : greenImage, position : Cartesian3.fromDegrees(-50.0, -50.0) }); var two = billboards.add({ image : greenImage, position : Cartesian3.fromDegrees(-50.0, 50.0) }); var maxRadii = ellipsoid.maximumRadius; var orthoFrustum = new OrthographicOffCenterFrustum(); orthoFrustum.right = maxRadii * Math.PI; orthoFrustum.left = -orthoFrustum.right; orthoFrustum.top = orthoFrustum.right; orthoFrustum.bottom = -orthoFrustum.top; orthoFrustum.near = 0.01 * maxRadii; orthoFrustum.far = 60.0 * maxRadii; camera.setView({ destination : Rectangle.fromDegrees(-60.0, -60.0, -40.0, 60.0) }); // Update scene state scene.morphTo2D(0); scene.renderForSpecs(); camera.frustum = orthoFrustum; scene.renderForSpecs(); var actual = scene.frameState.commandList[0].boundingVolume; var projectedPositions = [ projection.project(ellipsoid.cartesianToCartographic(one.position)), projection.project(ellipsoid.cartesianToCartographic(two.position)) ]; var expected = BoundingSphere.fromPoints(projectedPositions); expected.center = new Cartesian3(0.0, expected.center.x, expected.center.y); expect(actual.center).toEqualEpsilon(expected.center, CesiumMath.EPSILON8); expect(actual.radius).toBeGreaterThan(expected.radius); }); it('computes bounding sphere with pixel offset', function() { var one = billboards.add({ image : greenImage, position : Cartesian3.fromDegrees(-50.0, -50.0), pixelOffset : new Cartesian2(0.0, 200.0) }); var two = billboards.add({ image : greenImage, position : Cartesian3.fromDegrees(-50.0, 50.0), pixelOffset : new Cartesian2(0.0, 200.0) }); scene.renderForSpecs(); var actual = scene.frameState.commandList[0].boundingVolume; var positions = [one.position, two.position]; var bs = BoundingSphere.fromPoints(positions); var dimensions = new Cartesian2(1.0, 1.0); var diff = Cartesian3.subtract(actual.center, camera.position, new Cartesian3()); var vectorProjection = Cartesian3.multiplyByScalar(camera.direction, Cartesian3.dot(diff, camera.direction), new Cartesian3()); var distance = Math.max(0.0, Cartesian3.magnitude(vectorProjection) - bs.radius); var pixelSize = camera.frustum.getPixelDimensions(dimensions.x, dimensions.y, distance, new Cartesian2()); bs.radius += pixelSize.y * 0.25 * Math.max(greenImage.width, greenImage.height) + pixelSize.y * one.pixelOffset.y; expect(actual.center).toEqual(bs.center); expect(actual.radius).toEqual(bs.radius); }); it('computes bounding sphere with non-centered origin', function() { billboards.add({ image : greenImage, position : Cartesian3.fromDegrees(-50.0, -50.0) }); scene.renderForSpecs(); var centeredRadius = scene.frameState.commandList[0].boundingVolume.radius; billboards.removeAll(); billboards.add({ image : greenImage, position : Cartesian3.fromDegrees(-50.0, -50.0), verticalOrigin: VerticalOrigin.TOP }); scene.renderForSpecs(); var verticalRadius = scene.frameState.commandList[0].boundingVolume.radius; billboards.removeAll(); billboards.add({ image : greenImage, position : Cartesian3.fromDegrees(-50.0, -50.0), horizontalOrigin: HorizontalOrigin.LEFT }); scene.renderForSpecs(); var horizontalRadius = scene.frameState.commandList[0].boundingVolume.radius; expect(verticalRadius).toEqual(2*centeredRadius); expect(horizontalRadius).toEqual(2*centeredRadius); }); it('can create a billboard using a URL', function() { scene.renderForSpecs(); var one = billboards.add({ image : './Data/Images/Green2x2.png' }); expect(one.ready).toEqual(false); expect(one.image).toEqual('./Data/Images/Green2x2.png'); return pollToPromise(function() { return one.ready; }).then(function() { expect(scene).toRender([0, 255, 0, 255]); }); }); it('sets billboard width and height based on loaded image width and height', function() { scene.renderForSpecs(); var one = billboards.add({ image : './Data/Images/Green1x4.png' }); expect(one.width).toBeUndefined(); expect(one.height).toBeUndefined(); return pollToPromise(function() { return one.ready; }).then(function() { expect(one.width).toEqual(1); expect(one.height).toEqual(4); one.image = './Data/Images/Blue10x10.png'; return pollToPromise(function() { return one.ready; }).then(function() { expect(one.width).toEqual(10); expect(one.height).toEqual(10); }); }); }); it('does not cancel image load when a billboard is set to the same URL repeatedly', function() { scene.renderForSpecs(); var one = billboards.add({ image : './Data/Images/Green.png' }); expect(one.ready).toEqual(false); expect(one.image).toEqual('./Data/Images/Green.png'); one.image = './Data/Images/Green.png'; one.image = './Data/Images/Green.png'; one.image = './Data/Images/Green.png'; return pollToPromise(function() { return one.ready; }); }); it('ignores calls to set image equal to the current value after load', function() { scene.renderForSpecs(); var one = billboards.add({ image : './Data/Images/Green2x2.png' }); expect(one.ready).toEqual(false); expect(one.image).toEqual('./Data/Images/Green2x2.png'); return pollToPromise(function() { return one.ready; }).then(function() { expect(scene).toRender([0, 255, 0, 255]); one.image = './Data/Images/Green2x2.png'; expect(one.ready).toEqual(true); expect(scene).toRender([0, 255, 0, 255]); }); }); it('can create a billboard using a function', function() { var one = billboards.add({ image : function() { return greenImage; } }); // the image property will be an autogenerated id if not provided expect(one.image).toBeDefined(); expect(scene).toRender([0, 255, 0, 255]); }); it('can create a billboard using a function and id', function() { var one = billboards.add({ imageId : 'Foo', image : function() { return greenImage; } }); // the image property will be an autogenerated id if not provided expect(one.image).toEqual('Foo'); expect(scene).toRender([0, 255, 0, 255]); }); it('can create a billboard using another billboard image', function() { var createImage = jasmine.createSpy('createImage').and.returnValue(greenImage); var one = billboards.add({ image : createImage }); scene.renderForSpecs(); expect(createImage.calls.count()).toEqual(1); var two = billboards.add({ image : one.image }); scene.renderForSpecs(); expect(two.image).toEqual(one.image); expect(createImage.calls.count()).toEqual(1); expect(scene).toRender([0, 255, 0, 255]); }); it('can create a billboard using a subregion of an image', function() { scene.renderForSpecs(); var one = billboards.add({ image : './Data/Images/Red16x16.png', imageSubRegion : new BoundingRectangle(0.0, 0.0, 2.0, 3.0) }); expect(one.ready).toEqual(false); return pollToPromise(function() { return one.ready; }).then(function() { expect(scene).toRender([255, 0, 0, 255]); }); }); it('sets billboard width and height based on subregion width and height', function() { scene.renderForSpecs(); var one = billboards.add({ image : './Data/Images/Red16x16.png', imageSubRegion : new BoundingRectangle(0.0, 0.0, 1.0, 2.0) }); expect(one.width).toBeUndefined(); expect(one.height).toBeUndefined(); return pollToPromise(function() { return one.ready; }).then(function() { expect(one.width).toEqual(1); expect(one.height).toEqual(2); }); }); it('can change image while an image is loading', function() { scene.renderForSpecs(); var one = billboards.add({ image : './Data/Images/Green.png' }); expect(one.ready).toEqual(false); expect(one.image).toEqual('./Data/Images/Green.png'); // switch to blue while green is in-flight one.image = './Data/Images/Blue10x10.png'; expect(one.ready).toEqual(false); expect(one.image).toEqual('./Data/Images/Blue10x10.png'); return pollToPromise(function() { return one.ready; }).then(function() { var deferred = when.defer(); // render and yield control several times to make sure the // green image doesn't clobber the blue var iterations = 10; function renderAndCheck() { expect(scene).toRender([0, 0, 255, 255]); if (iterations > 0) { --iterations; setTimeout(renderAndCheck, 1); } else { deferred.resolve(); } } renderAndCheck(); return deferred.promise; }); }); it('can set image to undefined while an image is loading', function() { scene.renderForSpecs(); var one = billboards.add({ image : './Data/Images/Green.png' }); expect(one.ready).toEqual(false); expect(one.image).toEqual('./Data/Images/Green.png'); // switch to undefined while green is in-flight one.image = undefined; expect(one.ready).toEqual(false); expect(one.image).toBeUndefined(); var deferred = when.defer(); // render and yield control several times to make sure the // green image never loads var iterations = 10; function renderAndCheck() { expect(scene).toRender([0, 0, 0, 255]); if (iterations > 0) { --iterations; setTimeout(renderAndCheck, 1); } else { deferred.resolve(); } } renderAndCheck(); return deferred.promise; }); it('does not crash when removing a billboard that is loading', function() { scene.renderForSpecs(); var one = billboards.add({ image : './Data/Images/Green.png' }); billboards.remove(one); var deferred = when.defer(); // render and yield control several times to make sure the // green image doesn't crash when it loads var iterations = 10; function renderAndCheck() { expect(scene).toRender([0, 0, 0, 255]); if (iterations > 0) { --iterations; setTimeout(renderAndCheck, 1); } else { deferred.resolve(); } } renderAndCheck(); return deferred.promise; }); it('can add a billboard without a globe', function() { scene.globe = undefined; var billboardsWithoutGlobe = new BillboardCollection({ scene : scene }); var position = Cartesian3.fromDegrees(-73.0, 40.0); var b = billboardsWithoutGlobe.add({ position : position }); scene.renderForSpecs(); expect(b.position).toEqual(position); expect(b._actualClampedPosition).toBeUndefined(); }); describe('height referenced billboards', function() { var billboardsWithHeight; beforeEach(function() { scene.globe = createGlobe(); billboardsWithHeight = new BillboardCollection({ scene : scene }); scene.primitives.add(billboardsWithHeight); }); it('explicitly constructs a billboard with height reference', function() { var b = billboardsWithHeight.add({ heightReference : HeightReference.CLAMP_TO_GROUND }); expect(b.heightReference).toEqual(HeightReference.CLAMP_TO_GROUND); }); it('set billboard height reference property', function() { var b = billboardsWithHeight.add(); b.heightReference = HeightReference.CLAMP_TO_GROUND; expect(b.heightReference).toEqual(HeightReference.CLAMP_TO_GROUND); }); it('creating with a height reference creates a height update callback', function() { billboardsWithHeight.add({ heightReference : HeightReference.CLAMP_TO_GROUND, position : Cartesian3.fromDegrees(-72.0, 40.0) }); expect(scene.globe.callback).toBeDefined(); }); it('set height reference property creates a height update callback', function() { var b = billboardsWithHeight.add({ position : Cartesian3.fromDegrees(-72.0, 40.0) }); b.heightReference = HeightReference.CLAMP_TO_GROUND; expect(scene.globe.callback).toBeDefined(); }); it('updates the callback when the height reference changes', function() { var b = billboardsWithHeight.add({ heightReference : HeightReference.CLAMP_TO_GROUND, position : Cartesian3.fromDegrees(-72.0, 40.0) }); expect(scene.globe.callback).toBeDefined(); b.heightReference = HeightReference.RELATIVE_TO_GROUND; expect(scene.globe.removedCallback).toEqual(true); expect(scene.globe.callback).toBeDefined(); scene.globe.removedCallback = false; b.heightReference = HeightReference.NONE; expect(scene.globe.removedCallback).toEqual(true); expect(scene.globe.callback).toBeUndefined(); }); it('changing the position updates the callback', function() { var b = billboardsWithHeight.add({ heightReference : HeightReference.CLAMP_TO_GROUND, position : Cartesian3.fromDegrees(-72.0, 40.0) }); expect(scene.globe.callback).toBeDefined(); b.position = Cartesian3.fromDegrees(-73.0, 40.0); expect(scene.globe.removedCallback).toEqual(true); expect(scene.globe.callback).toBeDefined(); }); it('callback updates the position', function() { var b = billboardsWithHeight.add({ heightReference : HeightReference.CLAMP_TO_GROUND, position : Cartesian3.fromDegrees(-72.0, 40.0) }); expect(scene.globe.callback).toBeDefined(); var cartographic = scene.globe.ellipsoid.cartesianToCartographic(b._clampedPosition); expect(cartographic.height).toEqual(0.0); scene.globe.callback(Cartesian3.fromDegrees(-72.0, 40.0, 100.0)); cartographic = scene.globe.ellipsoid.cartesianToCartographic(b._clampedPosition); expect(cartographic.height).toEqualEpsilon(100.0, CesiumMath.EPSILON9); //Setting position to zero should clear the clamped position. b.position = Cartesian3.ZERO; expect(b._clampedPosition).toBeUndefined(); }); it('changing the terrain provider', function() { var b = billboardsWithHeight.add({ heightReference : HeightReference.CLAMP_TO_GROUND, position : Cartesian3.fromDegrees(-72.0, 40.0) }); expect(scene.globe.callback).toBeDefined(); spyOn(b, '_updateClamping').and.callThrough(); var terrainProvider = new CesiumTerrainProvider({ url : 'made/up/url', requestVertexNormals : true }); scene.terrainProvider = terrainProvider; expect(b._updateClamping).toHaveBeenCalled(); }); it('height reference without a scene rejects', function() { expect(function() { return billboards.add({ heightReference : HeightReference.CLAMP_TO_GROUND, position : Cartesian3.fromDegrees(-72.0, 40.0) }); }).toThrowDeveloperError(); }); it('changing height reference without a scene throws DeveloperError', function() { var b = billboards.add({ position : Cartesian3.fromDegrees(-72.0, 40.0) }); expect(function() { b.heightReference = HeightReference.CLAMP_TO_GROUND; }).toThrowDeveloperError(); }); it('height reference without a globe rejects', function() { scene.globe = undefined; expect(function() { return billboardsWithHeight.add({ heightReference : HeightReference.CLAMP_TO_GROUND, position : Cartesian3.fromDegrees(-72.0, 40.0) }); }).toThrowDeveloperError(); }); it('changing height reference without a globe throws DeveloperError', function() { var b = billboardsWithHeight.add({ position : Cartesian3.fromDegrees(-72.0, 40.0) }); scene.globe = undefined; expect(function() { b.heightReference = HeightReference.CLAMP_TO_GROUND; }).toThrowDeveloperError(); }); }); }, 'WebGL');