import { BoundingRectangle } from "../../Source/Cesium.js";
import { BoundingSphere } from "../../Source/Cesium.js";
import { Cartesian2 } from "../../Source/Cesium.js";
import { Cartesian3 } from "../../Source/Cesium.js";
import { Color } from "../../Source/Cesium.js";
import { DistanceDisplayCondition } from "../../Source/Cesium.js";
import { Math as CesiumMath } from "../../Source/Cesium.js";
import { NearFarScalar } from "../../Source/Cesium.js";
import { Rectangle } from "../../Source/Cesium.js";
import { BlendOption } from "../../Source/Cesium.js";
import { PointPrimitive } from "../../Source/Cesium.js";
import { PointPrimitiveCollection } from "../../Source/Cesium.js";
import createScene from "../createScene.js";

describe(
  "Scene/PointPrimitiveCollection",
  function () {
    var scene;
    var camera;
    var pointPrimitives;

    beforeAll(function () {
      scene = createScene();
      camera = scene.camera;
    });

    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);
      pointPrimitives = new PointPrimitiveCollection();
      scene.primitives.add(pointPrimitives);
    });

    afterEach(function () {
      // pointPrimitives are destroyed by removeAll().
      scene.primitives.removeAll();
    });

    it("constructs a default pointPrimitive", function () {
      var p = pointPrimitives.add();
      expect(p.show).toEqual(true);
      expect(p.position).toEqual(Cartesian3.ZERO);
      expect(p.pixelSize).toEqual(10.0);
      expect(p.color.red).toEqual(1.0);
      expect(p.color.green).toEqual(1.0);
      expect(p.color.blue).toEqual(1.0);
      expect(p.color.alpha).toEqual(1.0);
      expect(p.outlineColor.red).toEqual(0.0);
      expect(p.outlineColor.green).toEqual(0.0);
      expect(p.outlineColor.blue).toEqual(0.0);
      expect(p.outlineColor.alpha).toEqual(0.0);
      expect(p.outlineWidth).toEqual(0.0);
      expect(p.scaleByDistance).not.toBeDefined();
      expect(p.translucencyByDistance).not.toBeDefined();
      expect(p.distanceDisplayCondition).not.toBeDefined();
      expect(p.disableDepthTestDistance).toEqual(0.0);
      expect(p.id).not.toBeDefined();
    });

    it("can add and remove before first render.", function () {
      var p = pointPrimitives.add();
      pointPrimitives.remove(p);
      scene.renderForSpecs();
    });

    it("explicitly constructs a pointPrimitive", function () {
      var p = pointPrimitives.add({
        show: false,
        position: new Cartesian3(1.0, 2.0, 3.0),
        pixelSize: 2.0,
        color: {
          red: 0.1,
          green: 0.2,
          blue: 0.3,
          alpha: 0.4,
        },
        outlineColor: {
          red: 0.5,
          green: 0.6,
          blue: 0.7,
          alpha: 0.8,
        },
        outlineWidth: 4.0,
        scaleByDistance: new NearFarScalar(1.0, 3.0, 1.0e6, 0.0),
        translucencyByDistance: new NearFarScalar(1.0, 1.0, 1.0e6, 0.0),
        distanceDisplayCondition: new DistanceDisplayCondition(10.0, 100.0),
        disableDepthTestDistance: 10.0,
        id: "id",
      });

      expect(p.show).toEqual(false);
      expect(p.position).toEqual(new Cartesian3(1.0, 2.0, 3.0));
      expect(p.pixelSize).toEqual(2.0);
      expect(p.color.red).toEqual(0.1);
      expect(p.color.green).toEqual(0.2);
      expect(p.color.blue).toEqual(0.3);
      expect(p.color.alpha).toEqual(0.4);
      expect(p.outlineColor.red).toEqual(0.5);
      expect(p.outlineColor.green).toEqual(0.6);
      expect(p.outlineColor.blue).toEqual(0.7);
      expect(p.outlineColor.alpha).toEqual(0.8);
      expect(p.outlineWidth).toEqual(4.0);
      expect(p.scaleByDistance).toEqual(
        new NearFarScalar(1.0, 3.0, 1.0e6, 0.0)
      );
      expect(p.translucencyByDistance).toEqual(
        new NearFarScalar(1.0, 1.0, 1.0e6, 0.0)
      );
      expect(p.distanceDisplayCondition).toEqual(
        new DistanceDisplayCondition(10.0, 100.0)
      );
      expect(p.disableDepthTestDistance).toEqual(10.0);
      expect(p.id).toEqual("id");
    });

    it("sets pointPrimitive properties", function () {
      var p = pointPrimitives.add();
      p.show = false;
      p.position = new Cartesian3(1.0, 2.0, 3.0);
      p.pixelSize = 2.0;
      p.color = new Color(0.1, 0.2, 0.3, 0.4);
      p.outlineColor = new Color(0.5, 0.6, 0.7, 0.8);
      p.outlineWidth = 4.0;
      p.scaleByDistance = new NearFarScalar(1.0e6, 3.0, 1.0e8, 0.0);
      p.translucencyByDistance = new NearFarScalar(1.0e6, 1.0, 1.0e8, 0.0);
      p.distanceDisplayCondition = new DistanceDisplayCondition(10.0, 100.0);
      p.disableDepthTestDistance = 10.0;

      expect(p.show).toEqual(false);
      expect(p.position).toEqual(new Cartesian3(1.0, 2.0, 3.0));
      expect(p.pixelSize).toEqual(2.0);
      expect(p.color.red).toEqual(0.1);
      expect(p.color.green).toEqual(0.2);
      expect(p.color.blue).toEqual(0.3);
      expect(p.color.alpha).toEqual(0.4);
      expect(p.outlineColor.red).toEqual(0.5);
      expect(p.outlineColor.green).toEqual(0.6);
      expect(p.outlineColor.blue).toEqual(0.7);
      expect(p.outlineColor.alpha).toEqual(0.8);
      expect(p.outlineWidth).toEqual(4.0);
      expect(p.scaleByDistance).toEqual(
        new NearFarScalar(1.0e6, 3.0, 1.0e8, 0.0)
      );
      expect(p.translucencyByDistance).toEqual(
        new NearFarScalar(1.0e6, 1.0, 1.0e8, 0.0)
      );
      expect(p.distanceDisplayCondition).toEqual(
        new DistanceDisplayCondition(10.0, 100.0)
      );
      expect(p.disableDepthTestDistance).toEqual(10.0);
    });

    it("is not destroyed", function () {
      expect(pointPrimitives.isDestroyed()).toEqual(false);
    });

    it("renders pointPrimitive in multiple passes", function () {
      pointPrimitives.add({
        position: Cartesian3.ZERO,
        color: Color.LIME,
      });
      camera.position = new Cartesian3(2.0, 0.0, 0.0);

      var frameState = scene.frameState;
      frameState.commandList.length = 0;
      pointPrimitives.blendOption = BlendOption.OPAQUE_AND_TRANSLUCENT;
      pointPrimitives.update(frameState);
      expect(frameState.commandList.length).toEqual(2);

      frameState.commandList.length = 0;
      pointPrimitives.blendOption = BlendOption.OPAQUE;
      pointPrimitives.update(frameState);
      expect(frameState.commandList.length).toEqual(1);

      frameState.commandList.length = 0;
      pointPrimitives.blendOption = BlendOption.TRANSLUCENT;
      pointPrimitives.update(frameState);
      expect(frameState.commandList.length).toEqual(1);
    });

    it("disables pointPrimitive scaleByDistance", function () {
      var p = pointPrimitives.add({
        scaleByDistance: new NearFarScalar(1.0, 3.0, 1.0e6, 0.0),
      });
      p.scaleByDistance = undefined;
      expect(p.scaleByDistance).not.toBeDefined();
    });

    it("disables pointPrimitive translucencyByDistance", function () {
      var p = pointPrimitives.add({
        translucencyByDistance: new NearFarScalar(1.0, 1.0, 1.0e6, 0.0),
      });
      p.translucencyByDistance = undefined;
      expect(p.translucencyByDistance).not.toBeDefined();
    });

    it("renders pointPrimitive with scaleByDistance", function () {
      pointPrimitives.add({
        position: Cartesian3.ZERO,
        scaleByDistance: new NearFarScalar(2.0, 1.0, 4.0, 0.0),
        color: Color.LIME,
      });

      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 pointPrimitive with translucencyByDistance", function () {
      pointPrimitives.add({
        position: Cartesian3.ZERO,
        translucencyByDistance: new NearFarScalar(2.0, 1.0, 4.0, 0.0),
        color: Color.LIME,
      });

      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("does not render pointPrimitive if show is false", function () {
      pointPrimitives.add({
        position: Cartesian3.ZERO,
        translucencyByDistance: new NearFarScalar(2.0, 1.0, 4.0, 0.0),
        color: Color.LIME,
      });

      camera.position = new Cartesian3(2.0, 0.0, 0.0);
      expect(scene).toRender([0, 255, 0, 255]);

      pointPrimitives.show = false;
      expect(scene).toRender([0, 0, 0, 255]);
    });

    it("throws scaleByDistance with nearDistance === farDistance", function () {
      var p = pointPrimitives.add();
      var scale = new NearFarScalar(2.0e5, 1.0, 2.0e5, 0.0);
      expect(function () {
        p.scaleByDistance = scale;
      }).toThrowDeveloperError();
    });

    it("throws new pointPrimitive with invalid scaleByDistance (nearDistance === farDistance)", function () {
      var scale = new NearFarScalar(2.0e5, 1.0, 2.0e5, 0.0);
      expect(function () {
        pointPrimitives.add({
          scaleByDistance: scale,
        });
      }).toThrowDeveloperError();
    });

    it("throws scaleByDistance with nearDistance > farDistance", function () {
      var p = pointPrimitives.add();
      var scale = new NearFarScalar(1.0e9, 1.0, 1.0e5, 1.0);
      expect(function () {
        p.scaleByDistance = scale;
      }).toThrowDeveloperError();
    });

    it("throws translucencyByDistance with nearDistance === farDistance", function () {
      var p = pointPrimitives.add();
      var translucency = new NearFarScalar(2.0e5, 1.0, 2.0e5, 0.0);
      expect(function () {
        p.translucencyByDistance = translucency;
      }).toThrowDeveloperError();
    });

    it("throws new pointPrimitive with invalid translucencyByDistance (nearDistance === farDistance)", function () {
      var translucency = new NearFarScalar(2.0e5, 1.0, 2.0e5, 0.0);
      expect(function () {
        pointPrimitives.add({
          translucencyByDistance: translucency,
        });
      }).toThrowDeveloperError();
    });

    it("throws translucencyByDistance with nearDistance > farDistance", function () {
      var p = pointPrimitives.add();
      var translucency = new NearFarScalar(1.0e9, 1.0, 1.0e5, 1.0);
      expect(function () {
        p.translucencyByDistance = translucency;
      }).toThrowDeveloperError();
    });

    it("renders pointPrimitive with distanceDisplayCondition", function () {
      pointPrimitives.add({
        position: Cartesian3.ZERO,
        color: Color.LIME,
        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 pointPrimitive with invalid distanceDisplayCondition (near >= far)", function () {
      var dc = new DistanceDisplayCondition(100.0, 10.0);
      expect(function () {
        pointPrimitives.add({
          distanceDisplayCondition: dc,
        });
      }).toThrowDeveloperError();
    });

    it("throws distanceDisplayCondition with near >= far", function () {
      var p = pointPrimitives.add();
      var dc = new DistanceDisplayCondition(100.0, 10.0);
      expect(function () {
        p.distanceDisplayCondition = dc;
      }).toThrowDeveloperError();
    });

    it("renders with disableDepthTestDistance", function () {
      var p = pointPrimitives.add({
        position: new Cartesian3(-1.0, 0.0, 0.0),
        pixelSize: 10.0,
        color: Color.LIME,
      });
      pointPrimitives.add({
        position: Cartesian3.ZERO,
        pixelSize: 10.0,
        color: Color.BLUE,
      });

      expect(scene).toRender([0, 0, 255, 255]);

      p.disableDepthTestDistance = Number.POSITIVE_INFINITY;
      expect(scene).toRender([0, 255, 0, 255]);
    });

    it("throws with new point primitive with invalid disableDepthTestDistance (< 0.0)", function () {
      expect(function () {
        pointPrimitives.add({
          disableDepthTestDistance: -1.0,
        });
      }).toThrowDeveloperError();
    });

    it("throws with disableDepthTestDistance set less than 0.0", function () {
      var p = pointPrimitives.add();
      expect(function () {
        p.disableDepthTestDistance = -1.0;
      }).toThrowDeveloperError();
    });

    it("set a removed pointPrimitive property", function () {
      var p = pointPrimitives.add();
      pointPrimitives.remove(p);
      p.show = false;
      expect(p.show).toEqual(false);
    });

    it("has zero pointPrimitives when constructed", function () {
      expect(pointPrimitives.length).toEqual(0);
    });

    it("adds a pointPrimitive", function () {
      var p = pointPrimitives.add({
        position: new Cartesian3(1.0, 2.0, 3.0),
      });

      expect(pointPrimitives.length).toEqual(1);
      expect(pointPrimitives.get(0)).toEqual(p);
    });

    it("removes the first pointPrimitive", function () {
      var one = pointPrimitives.add({
        position: new Cartesian3(1.0, 2.0, 3.0),
      });
      var two = pointPrimitives.add({
        position: new Cartesian3(4.0, 5.0, 6.0),
      });

      expect(pointPrimitives.length).toEqual(2);

      expect(pointPrimitives.remove(one)).toEqual(true);

      expect(pointPrimitives.length).toEqual(1);
      expect(pointPrimitives.get(0)).toEqual(two);
    });

    it("removes the last pointPrimitive", function () {
      var one = pointPrimitives.add({
        position: new Cartesian3(1.0, 2.0, 3.0),
      });
      var two = pointPrimitives.add({
        position: new Cartesian3(4.0, 5.0, 6.0),
      });

      expect(pointPrimitives.length).toEqual(2);

      expect(pointPrimitives.remove(two)).toEqual(true);

      expect(pointPrimitives.length).toEqual(1);
      expect(pointPrimitives.get(0)).toEqual(one);
    });

    it("removes the same pointPrimitive twice", function () {
      var p = pointPrimitives.add({
        position: new Cartesian3(1.0, 2.0, 3.0),
      });
      expect(pointPrimitives.length).toEqual(1);

      expect(pointPrimitives.remove(p)).toEqual(true);
      expect(pointPrimitives.length).toEqual(0);

      expect(pointPrimitives.remove(p)).toEqual(false);
      expect(pointPrimitives.length).toEqual(0);
    });

    it("returns false when removing undefined", function () {
      pointPrimitives.add({
        position: new Cartesian3(1.0, 2.0, 3.0),
      });
      expect(pointPrimitives.length).toEqual(1);

      expect(pointPrimitives.remove(undefined)).toEqual(false);
      expect(pointPrimitives.length).toEqual(1);
    });

    it("adds and removes pointPrimitives", function () {
      var one = pointPrimitives.add({
        position: new Cartesian3(1.0, 2.0, 3.0),
      });
      var two = pointPrimitives.add({
        position: new Cartesian3(4.0, 5.0, 6.0),
      });
      expect(pointPrimitives.length).toEqual(2);
      expect(pointPrimitives.get(0)).toEqual(one);
      expect(pointPrimitives.get(1)).toEqual(two);

      expect(pointPrimitives.remove(two)).toEqual(true);
      var three = pointPrimitives.add({
        position: new Cartesian3(7.0, 8.0, 9.0),
      });
      expect(pointPrimitives.length).toEqual(2);
      expect(pointPrimitives.get(0)).toEqual(one);
      expect(pointPrimitives.get(1)).toEqual(three);
    });

    it("removes all pointPrimitives", function () {
      pointPrimitives.add({
        position: new Cartesian3(1.0, 2.0, 3.0),
      });
      pointPrimitives.add({
        position: new Cartesian3(4.0, 5.0, 6.0),
      });
      expect(pointPrimitives.length).toEqual(2);

      pointPrimitives.removeAll();
      expect(pointPrimitives.length).toEqual(0);
    });

    it("can check if it contains a pointPrimitive", function () {
      var pointPrimitive = pointPrimitives.add();

      expect(pointPrimitives.contains(pointPrimitive)).toEqual(true);
    });

    it("returns false when checking if it contains a pointPrimitive it does not contain", function () {
      var pointPrimitive = pointPrimitives.add();
      pointPrimitives.remove(pointPrimitive);

      expect(pointPrimitives.contains(pointPrimitive)).toEqual(false);
    });

    it("does not contain undefined", function () {
      expect(pointPrimitives.contains(undefined)).toEqual(false);
    });

    it("does not contain random other objects", function () {
      expect(pointPrimitives.contains({})).toEqual(false);
      expect(pointPrimitives.contains(new Cartesian2())).toEqual(false);
    });

    it("does not render when constructed", function () {
      expect(scene).toRender([0, 0, 0, 255]);
    });

    it("modifies and removes a pointPrimitive, then renders", function () {
      var p1 = pointPrimitives.add({
        position: Cartesian3.ZERO,
        color: Color.LIME,
      });
      pointPrimitives.add({
        position: new Cartesian3(-1.0, 0.0, 0.0),
        color: Color.BLUE,
      });

      expect(scene).toRender([0, 255, 0, 255]);

      p1.pixelSize = 2.0;
      pointPrimitives.remove(p1);
      expect(scene).toRender([0, 0, 255, 255]);
    });

    it("renders a green pointPrimitive", function () {
      pointPrimitives.add({
        position: Cartesian3.ZERO,
        color: Color.LIME,
      });

      expect(scene).toRender([0, 255, 0, 255]);
    });

    it("adds and renders a pointPrimitive", function () {
      pointPrimitives.add({
        position: Cartesian3.ZERO,
        color: Color.LIME,
      });

      expect(scene).toRender([0, 255, 0, 255]);

      pointPrimitives.add({
        position: new Cartesian3(1.0, 0.0, 0.0), // Closer to camera
        color: Color.BLUE,
      });

      expect(scene).toRender([0, 0, 255, 255]);
    });

    it("removes and renders a pointPrimitive", function () {
      pointPrimitives.add({
        position: Cartesian3.ZERO,
        color: Color.LIME,
      });
      var bluePointPrimitive = pointPrimitives.add({
        position: new Cartesian3(1.0, 0.0, 0.0), // Closer to camera
        color: Color.BLUE,
      });

      expect(scene).toRender([0, 0, 255, 255]);

      pointPrimitives.remove(bluePointPrimitive);
      expect(scene).toRender([0, 255, 0, 255]);
    });

    it("removes all pointPrimitives and renders", function () {
      pointPrimitives.add({
        position: Cartesian3.ZERO,
        color: Color.LIME,
      });

      expect(scene).toRender([0, 255, 0, 255]);

      pointPrimitives.removeAll();
      expect(scene).toRender([0, 0, 0, 255]);
    });

    it("removes all pointPrimitives, adds a pointPrimitive, and renders", function () {
      pointPrimitives.add({
        position: Cartesian3.ZERO,
        color: Color.LIME,
      });

      expect(scene).toRender([0, 255, 0, 255]);

      pointPrimitives.removeAll();
      pointPrimitives.add({
        position: Cartesian3.ZERO,
        color: Color.BLUE,
      });

      expect(scene).toRender([0, 0, 255, 255]);
    });

    it("renders using pointPrimitive show property", function () {
      var greenPointPrimitive = pointPrimitives.add({
        position: Cartesian3.ZERO,
        color: Color.LIME,
      });
      var bluePointPrimitive = pointPrimitives.add({
        show: false,
        position: Cartesian3.ZERO,
        color: Color.BLUE,
      });

      expect(scene).toRender([0, 255, 0, 255]);

      greenPointPrimitive.show = false;
      bluePointPrimitive.show = true;

      expect(scene).toRender([0, 0, 255, 255]);
    });

    it("renders using pointPrimitive position property", function () {
      var p = pointPrimitives.add({
        position: Cartesian3.ZERO,
        color: Color.LIME,
      });

      expect(scene).toRender([0, 255, 0, 255]);

      p.position = new Cartesian3(20.0, 0.0, 0.0); // Behind camera
      expect(scene).toRender([0, 0, 0, 255]);

      p.position = new Cartesian3(1.0, 0.0, 0.0); // Back in front of camera
      expect(scene).toRender([0, 255, 0, 255]);
    });

    it("renders using pointPrimitive color property", function () {
      var p = pointPrimitives.add({
        position: Cartesian3.ZERO,
        color: Color.WHITE,
      });

      expect(scene).toRender([255, 255, 255, 255]);

      p.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
      p.color = new Color(0.0, 1.0, 0.0, 1.0);
      expect(scene).toRender([0, 255, 0, 255]);
    });

    it("renders bounding volume with debugShowBoundingVolume", function () {
      pointPrimitives.add({
        position: Cartesian3.ZERO,
        color: Color.LIME,
        pixelSize: 0.5, // bring bounding volume in view
      });
      pointPrimitives.debugShowBoundingVolume = true;

      expect(scene).notToRender([0, 0, 0, 255]);
    });

    it("updates 10% of pointPrimitives", function () {
      for (var i = 0; i < 10; ++i) {
        pointPrimitives.add({
          position: Cartesian3.ZERO,
          color: Color.WHITE,
          show: i === 3,
        });
      }

      // First render - default pointPrimitive color is white.
      expect(scene).toRender([255, 255, 255, 255]);

      pointPrimitives.get(3).color = new Color(0.0, 1.0, 0.0, 1.0);

      // Second render - pointPrimitive is green
      expect(scene).toRender([0, 255, 0, 255]);

      pointPrimitives.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 64K pointPrimitives", function () {
      for (var i = 0; i < 64 * 1024; ++i) {
        pointPrimitives.add({
          position: Cartesian3.ZERO,
          color: Color.TRANSPARENT,
        });
      }

      pointPrimitives.add({
        position: Cartesian3.ZERO,
        color: Color.WHITE,
      });

      expect(scene).toRender([255, 255, 255, 255]);
    });

    it("is picked", function () {
      var p = pointPrimitives.add({
        position: Cartesian3.ZERO,
        color: Color.WHITE,
        id: "id",
      });

      expect(scene).toPickAndCall(function (result) {
        expect(result.primitive).toEqual(p);
        expect(result.id).toEqual("id");
      });
    });

    it("can change pick id", function () {
      var p = pointPrimitives.add({
        position: Cartesian3.ZERO,
        color: Color.WHITE,
        id: "id",
      });

      expect(scene).toPickAndCall(function (result) {
        expect(result.primitive).toEqual(p);
        expect(result.id).toEqual("id");
      });

      p.id = "id2";

      expect(scene).toPickAndCall(function (result) {
        expect(result.primitive).toEqual(p);
        expect(result.id).toEqual("id2");
      });
    });

    it("is not picked", function () {
      pointPrimitives.add({
        show: false,
        position: Cartesian3.ZERO,
        color: Color.WHITE,
      });

      expect(scene).notToPick();
    });

    it("picks a pointPrimitive using scaleByDistance", function () {
      var p = pointPrimitives.add({
        position: Cartesian3.ZERO,
        color: Color.WHITE,
      });

      var scaleByDistance = new NearFarScalar(1.0, 4.0, 3.0e9, 2.0);
      p.scaleByDistance = scaleByDistance;

      expect(scene).toPickPrimitive(p);

      scaleByDistance.nearValue = 0.0;
      scaleByDistance.farValue = 0.0;
      p.scaleByDistance = scaleByDistance;

      expect(scene).notToPick();
    });

    it("picks a pointPrimitive using translucencyByDistance", function () {
      var p = pointPrimitives.add({
        position: Cartesian3.ZERO,
        color: Color.WHITE,
      });

      var translucency = new NearFarScalar(1.0, 0.9, 3.0e9, 0.8);
      p.translucencyByDistance = translucency;

      expect(scene).toPickPrimitive(p);

      translucency.nearValue = 0.0;
      translucency.farValue = 0.0;
      p.translucencyByDistance = translucency;

      expect(scene).notToPick();
    });

    it("computes screen space position", function () {
      var p = pointPrimitives.add({
        position: Cartesian3.ZERO,
      });
      scene.renderForSpecs();
      expect(p.computeScreenSpacePosition(scene)).toEqualEpsilon(
        new Cartesian2(0.5, 0.5),
        CesiumMath.EPSILON1
      );
    });

    it("stores screen space position in a result", function () {
      var p = pointPrimitives.add({
        position: Cartesian3.ZERO,
      });
      var result = new Cartesian2();
      scene.renderForSpecs();
      var actual = p.computeScreenSpacePosition(scene, result);
      expect(actual).toEqual(result);
      expect(result).toEqualEpsilon(
        new Cartesian2(0.5, 0.5),
        CesiumMath.EPSILON1
      );
    });

    it("throws when computing screen space position when not in a collection", function () {
      var p = pointPrimitives.add({
        position: Cartesian3.ZERO,
      });
      pointPrimitives.remove(p);
      expect(function () {
        p.computeScreenSpacePosition(scene);
      }).toThrowDeveloperError();
    });

    it("throws when computing screen space position without scene", function () {
      var p = pointPrimitives.add();

      expect(function () {
        p.computeScreenSpacePosition();
      }).toThrowDeveloperError();
    });

    it("computes screen space bounding box", function () {
      var size = 10;

      var p = pointPrimitives.add({
        size: size,
      });

      var halfWidth = size * 0.5;
      var halfHeight = halfWidth;

      var bbox = PointPrimitive.getScreenSpaceBoundingBox(p, Cartesian2.ZERO);
      expect(bbox.x).toEqual(-halfWidth);
      expect(bbox.y).toEqual(-halfHeight);
      expect(bbox.width).toEqual(size);
      expect(bbox.height).toEqual(size);
    });

    it("computes screen space bounding box with result", function () {
      var size = 10;

      var p = pointPrimitives.add({
        size: size,
      });

      var halfWidth = size * 0.5;
      var halfHeight = halfWidth;

      var result = new BoundingRectangle();
      var bbox = PointPrimitive.getScreenSpaceBoundingBox(
        p,
        Cartesian2.ZERO,
        result
      );
      expect(bbox.x).toEqual(-halfWidth);
      expect(bbox.y).toEqual(-halfHeight);
      expect(bbox.width).toEqual(size);
      expect(bbox.height).toEqual(size);
      expect(bbox).toBe(result);
    });

    it("equals another pointPrimitive", function () {
      var p = pointPrimitives.add({
        position: new Cartesian3(1.0, 2.0, 3.0),
        color: {
          red: 1.0,
          green: 0.0,
          blue: 0.0,
          alpha: 1.0,
        },
      });
      var p2 = pointPrimitives.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 `PointPrimitiveCollection.equals` function itself, not simple equality.
      expect(p.equals(p2)).toEqual(true);
    });

    it("does not equal another pointPrimitive", function () {
      var p = pointPrimitives.add({
        position: new Cartesian3(1.0, 2.0, 3.0),
      });
      var p2 = pointPrimitives.add({
        position: new Cartesian3(4.0, 5.0, 6.0),
      });

      // This tests the `PointPrimitiveCollection.equals` function itself, not simple equality.
      expect(p.equals(p2)).toEqual(false);
    });

    it("does not equal undefined", function () {
      // This tests the `PointPrimitiveCollection.equals` function itself, not simple equality.
      var pointPrimitive = pointPrimitives.add();
      expect(pointPrimitive.equals(undefined)).toEqual(false);
    });

    it("throws when accessing without an index", function () {
      expect(function () {
        pointPrimitives.get();
      }).toThrowDeveloperError();
    });

    it("computes bounding sphere in 3D", function () {
      var one = pointPrimitives.add({
        position: Cartesian3.fromDegrees(-50.0, -50.0),
      });
      var two = pointPrimitives.add({
        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 = pointPrimitives.add({
        position: Cartesian3.fromDegrees(-50.0, -50.0),
      });
      var two = pointPrimitives.add({
        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 = pointPrimitives.add({
        color: Color.LIME,
        position: Cartesian3.fromDegrees(-50.0, -50.0),
      });
      var two = pointPrimitives.add({
        color: Color.LIME,
        position: Cartesian3.fromDegrees(-50.0, 50.0),
      });

      camera.setView({
        destination: Rectangle.fromDegrees(-60.0, -60.0, -40.0, 60.0),
      });

      scene.morphTo2D(0);
      scene.renderForSpecs();

      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);
    });
  },
  "WebGL"
);