import { arraySlice } from "../../Source/Cesium.js";
import { AttributeCompression } from "../../Source/Cesium.js";
import { BoundingSphere } from "../../Source/Cesium.js";
import { BoxGeometry } from "../../Source/Cesium.js";
import { Cartesian2 } from "../../Source/Cesium.js";
import { Cartesian3 } from "../../Source/Cesium.js";
import { ComponentDatatype } from "../../Source/Cesium.js";
import { Ellipsoid } from "../../Source/Cesium.js";
import { EllipsoidGeometry } from "../../Source/Cesium.js";
import { EncodedCartesian3 } from "../../Source/Cesium.js";
import { GeographicProjection } from "../../Source/Cesium.js";
import { Geometry } from "../../Source/Cesium.js";
import { GeometryAttribute } from "../../Source/Cesium.js";
import { GeometryInstance } from "../../Source/Cesium.js";
import { GeometryPipeline } from "../../Source/Cesium.js";
import { GeometryType } from "../../Source/Cesium.js";
import { Math as CesiumMath } from "../../Source/Cesium.js";
import { Matrix4 } from "../../Source/Cesium.js";
import { PolygonGeometry } from "../../Source/Cesium.js";
import { PrimitiveType } from "../../Source/Cesium.js";
import { Tipsify } from "../../Source/Cesium.js";
import { VertexFormat } from "../../Source/Cesium.js";

describe("Core/GeometryPipeline", function () {
  it("converts triangles to wireframe in place", function () {
    var geometry = GeometryPipeline.toWireframe(
      new Geometry({
        attributes: {},
        indices: [0, 1, 2, 3, 4, 5],
        primitiveType: PrimitiveType.TRIANGLES,
      })
    );

    expect(geometry.primitiveType).toEqual(PrimitiveType.LINES);

    var v = geometry.indices;
    expect(v.length).toEqual(12);

    expect(v[0]).toEqual(0);
    expect(v[1]).toEqual(1);
    expect(v[2]).toEqual(1);
    expect(v[3]).toEqual(2);
    expect(v[4]).toEqual(2);
    expect(v[5]).toEqual(0);

    expect(v[6]).toEqual(3);
    expect(v[7]).toEqual(4);
    expect(v[8]).toEqual(4);
    expect(v[9]).toEqual(5);
    expect(v[10]).toEqual(5);
    expect(v[11]).toEqual(3);
  });

  it("converts a triangle fan to wireframe in place", function () {
    var geometry = GeometryPipeline.toWireframe(
      new Geometry({
        attributes: {},
        indices: [0, 1, 2, 3],
        primitiveType: PrimitiveType.TRIANGLE_FAN,
      })
    );

    expect(geometry.primitiveType).toEqual(PrimitiveType.LINES);

    var v = geometry.indices;
    expect(v.length).toEqual(12);

    expect(v[0]).toEqual(0);
    expect(v[1]).toEqual(1);
    expect(v[2]).toEqual(1);
    expect(v[3]).toEqual(2);
    expect(v[4]).toEqual(2);
    expect(v[5]).toEqual(0);

    expect(v[6]).toEqual(0);
    expect(v[7]).toEqual(2);
    expect(v[8]).toEqual(2);
    expect(v[9]).toEqual(3);
    expect(v[10]).toEqual(3);
    expect(v[11]).toEqual(0);
  });

  it("converts a triangle strip to wireframe in place", function () {
    var geometry = GeometryPipeline.toWireframe(
      new Geometry({
        attributes: {},
        indices: [0, 1, 2, 3],
        primitiveType: PrimitiveType.TRIANGLE_STRIP,
      })
    );

    expect(geometry.primitiveType).toEqual(PrimitiveType.LINES);

    var v = geometry.indices;
    expect(v.length).toEqual(12);

    expect(v[0]).toEqual(0);
    expect(v[1]).toEqual(1);
    expect(v[2]).toEqual(1);
    expect(v[3]).toEqual(2);
    expect(v[4]).toEqual(2);
    expect(v[5]).toEqual(0);

    expect(v[6]).toEqual(2);
    expect(v[7]).toEqual(3);
    expect(v[8]).toEqual(3);
    expect(v[9]).toEqual(1);
    expect(v[10]).toEqual(1);
    expect(v[11]).toEqual(2);
  });

  it("toWireframe throws without a geometry", function () {
    expect(function () {
      GeometryPipeline.toWireframe(undefined);
    }).toThrowDeveloperError();
  });

  it("toWireframe throws when primitiveType is not a triangle type", function () {
    expect(function () {
      GeometryPipeline.toWireframe(
        new Geometry({
          attributes: {},
          indices: [],
          primitiveType: PrimitiveType.POINTS,
        })
      );
    }).toThrowDeveloperError();
  });

  it("createLineSegmentsForVectors", function () {
    var geometry = new Geometry({
      attributes: {
        position: new GeometryAttribute({
          componentDatatype: ComponentDatatype.FLOAT,
          componentsPerAttribute: 3,
          values: [0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0],
        }),
        normal: new GeometryAttribute({
          componentDatatype: ComponentDatatype.FLOAT,
          componentsPerAttribute: 3,
          values: [0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0],
        }),
      },
      primitiveType: PrimitiveType.TRIANGLES,
      boundingSphere: new BoundingSphere(new Cartesian3(0.5, 0.5, 0.0), 1.0),
    });
    var lines = GeometryPipeline.createLineSegmentsForVectors(
      geometry,
      "normal",
      1.0
    );
    var linePositions = [
      0.0,
      0.0,
      0.0,
      0.0,
      0.0,
      1.0,
      1.0,
      0.0,
      0.0,
      1.0,
      0.0,
      1.0,
      0.0,
      1.0,
      0.0,
      0.0,
      1.0,
      1.0,
    ];

    expect(lines.attributes).toBeDefined();
    expect(lines.attributes.position).toBeDefined();
    expect(lines.attributes.position.values).toEqual(linePositions);
    expect(lines.primitiveType).toEqual(PrimitiveType.LINES);
    expect(lines.boundingSphere.center).toEqual(geometry.boundingSphere.center);
    expect(lines.boundingSphere.radius).toEqual(
      geometry.boundingSphere.radius + 1.0
    );
  });

  it("createLineSegmentsForVectors throws without geometry", function () {
    expect(function () {
      GeometryPipeline.createLineSegmentsForVectors();
    }).toThrowDeveloperError();
  });

  it("createLineSegmentsForVectors throws without geometry.attributes.position", function () {
    expect(function () {
      GeometryPipeline.createLineSegmentsForVectors();
    }).toThrowDeveloperError();
  });

  it("createLineSegmentsForVectors throws when geometry.attributes does not have an attributeName property", function () {
    var geometry = new Geometry({
      attributes: {
        position: new GeometryAttribute({
          componentDatatype: ComponentDatatype.FLOAT,
          componentsPerAttribute: 3,
          values: [0.0, 1.0, 2.0, 3.0, 4.0, 5.0],
        }),
      },
      primitiveType: PrimitiveType.TRIANGLES,
    });

    expect(function () {
      GeometryPipeline.createLineSegmentsForVectors(geometry, "bitangent");
    }).toThrowDeveloperError();
  });

  it("creates attribute indices", function () {
    var geometry = new Geometry({
      attributes: {
        position: new GeometryAttribute({
          componentDatatype: ComponentDatatype.FLOAT,
          componentsPerAttribute: 3,
          values: [],
        }),
        normal: new GeometryAttribute({
          componentDatatype: ComponentDatatype.FLOAT,
          componentsPerAttribute: 3,
          values: [],
        }),
        color: new GeometryAttribute({
          componentDatatype: ComponentDatatype.UNSIGNED_BYTE,
          componentsPerAttribute: 4,
          values: [],
        }),
      },
      primitiveType: PrimitiveType.TRIANGLES,
    });

    var indices = GeometryPipeline.createAttributeLocations(geometry);

    var validIndices = [0, 1, 2];
    expect(validIndices).toContain(indices.position);
    expect(validIndices).toContain(indices.normal);
    expect(validIndices).toContain(indices.color);
    expect(indices.position).not.toEqual(indices.normal);
    expect(indices.position).not.toEqual(indices.color);
  });

  it("createAttributeLocations throws without a geometry", function () {
    expect(function () {
      GeometryPipeline.createAttributeLocations(undefined);
    }).toThrowDeveloperError();
  });

  it("reorderForPreVertexCache reorders all indices and attributes for the pre vertex cache", function () {
    var geometry = new Geometry({
      attributes: {
        weight: new GeometryAttribute({
          componentDatatype: ComponentDatatype.FLOAT,
          componentsPerAttribute: 1,
          values: [0.0, 1.0, 2.0, 3.0, 4.0, 5.0],
        }),
        positions: new GeometryAttribute({
          componentDatatype: ComponentDatatype.FLOAT,
          componentsPerAttribute: 3,
          values: [
            0.0,
            1.0,
            2.0,
            3.0,
            4.0,
            5.0,
            6.0,
            7.0,
            8.0,
            9.0,
            10.0,
            11.0,
            12.0,
            13.0,
            14.0,
            15.0,
            16.0,
            17.0,
          ],
        }),
      },
      indices: [5, 3, 2, 0, 1, 4, 4, 1, 3, 2, 5, 0],
      primitiveType: PrimitiveType.TRIANGLES,
    });

    GeometryPipeline.reorderForPreVertexCache(geometry);

    expect(geometry.indices[0]).toEqual(0);
    expect(geometry.indices[1]).toEqual(1);
    expect(geometry.indices[2]).toEqual(2);
    expect(geometry.indices[3]).toEqual(3);
    expect(geometry.indices[4]).toEqual(4);
    expect(geometry.indices[5]).toEqual(5);
    expect(geometry.indices[6]).toEqual(5);
    expect(geometry.indices[7]).toEqual(4);
    expect(geometry.indices[8]).toEqual(1);
    expect(geometry.indices[9]).toEqual(2);
    expect(geometry.indices[10]).toEqual(0);
    expect(geometry.indices[11]).toEqual(3);

    expect(geometry.attributes.weight.values[0]).toEqual(5.0);
    expect(geometry.attributes.weight.values[1]).toEqual(3.0);
    expect(geometry.attributes.weight.values[2]).toEqual(2.0);
    expect(geometry.attributes.weight.values[3]).toEqual(0.0);
    expect(geometry.attributes.weight.values[4]).toEqual(1.0);
    expect(geometry.attributes.weight.values[5]).toEqual(4.0);

    expect(geometry.attributes.positions.values[0]).toEqual(15);
    expect(geometry.attributes.positions.values[1]).toEqual(16);
    expect(geometry.attributes.positions.values[2]).toEqual(17);
    expect(geometry.attributes.positions.values[3]).toEqual(9);
    expect(geometry.attributes.positions.values[4]).toEqual(10);
    expect(geometry.attributes.positions.values[5]).toEqual(11);
    expect(geometry.attributes.positions.values[6]).toEqual(6);
    expect(geometry.attributes.positions.values[7]).toEqual(7);
    expect(geometry.attributes.positions.values[8]).toEqual(8);
    expect(geometry.attributes.positions.values[9]).toEqual(0);
    expect(geometry.attributes.positions.values[10]).toEqual(1);
    expect(geometry.attributes.positions.values[11]).toEqual(2);
    expect(geometry.attributes.positions.values[12]).toEqual(3);
    expect(geometry.attributes.positions.values[13]).toEqual(4);
    expect(geometry.attributes.positions.values[14]).toEqual(5);
    expect(geometry.attributes.positions.values[15]).toEqual(12);
    expect(geometry.attributes.positions.values[16]).toEqual(13);
    expect(geometry.attributes.positions.values[17]).toEqual(14);
  });

  it("reoderForPreVertexCache removes unused vertices", function () {
    var geometry = new Geometry({
      attributes: {
        weight: new GeometryAttribute({
          componentDatatype: ComponentDatatype.FLOAT,
          componentsPerAttribute: 1,
          values: [0.0, 1.0, 2.0, 3.0, 4.0, 5.0],
        }),
        positions: new GeometryAttribute({
          componentDatatype: ComponentDatatype.FLOAT,
          componentsPerAttribute: 3,
          values: [
            0.0,
            1.0,
            2.0,
            3.0,
            4.0,
            5.0,
            6.0,
            7.0,
            8.0,
            9.0,
            10.0,
            11.0,
            12.0,
            13.0,
            14.0,
            15.0,
            16.0,
            17.0,
          ],
        }),
      },
      indices: [5, 3, 2, 4, 1, 3],
      primitiveType: PrimitiveType.TRIANGLES,
    });

    GeometryPipeline.reorderForPreVertexCache(geometry);

    expect(geometry.indices.length).toEqual(6);
    expect(geometry.attributes.positions.values.length).toEqual((6 - 1) * 3);
    expect(geometry.attributes.weight.values.length).toEqual(6 - 1);
  });

  it("reorderForPreVertexCache throws without a geometry", function () {
    expect(function () {
      GeometryPipeline.reorderForPreVertexCache(undefined);
    }).toThrowDeveloperError();
  });

  it("reorderForPreVertexCache throws when attributes have a different number of attributes", function () {
    expect(function () {
      var geometry = new Geometry({
        attributes: {
          attribute1: new GeometryAttribute({
            componentDatatype: ComponentDatatype.FLOAT,
            componentsPerAttribute: 1,
            values: [0, 1, 2],
          }),
          attribute2: new GeometryAttribute({
            componentDatatype: ComponentDatatype.FLOAT,
            componentsPerAttribute: 3,
            values: [0, 1, 2, 3, 4, 5],
          }),
        },
      });

      GeometryPipeline.reorderForPreVertexCache(geometry);
    }).toThrowDeveloperError();
  });

  it("reorderForPostVertexCache reorders indices for the post vertex cache", function () {
    var geometry = EllipsoidGeometry.createGeometry(new EllipsoidGeometry());
    var acmrBefore = Tipsify.calculateACMR({
      indices: geometry.indices,
      cacheSize: 24,
    });
    expect(acmrBefore).toBeGreaterThan(1.0);
    geometry = GeometryPipeline.reorderForPostVertexCache(geometry);
    var acmrAfter = Tipsify.calculateACMR({
      indices: geometry.indices,
      cacheSize: 24,
    });
    expect(acmrAfter).toBeLessThan(0.7);
  });

  it("reorderForPostVertexCache throws without a geometry", function () {
    expect(function () {
      GeometryPipeline.reorderForPostVertexCache(undefined);
    }).toThrowDeveloperError();
  });

  it("fitToUnsignedShortIndicestoThrowDeveloperErrorot change geometry", function () {
    var geometry = new Geometry({
      attributes: {
        time: new GeometryAttribute({
          componentDatatype: ComponentDatatype.FLOAT,
          componentsPerAttribute: 1,
          values: [10.0],
        }),
        heat: new GeometryAttribute({
          componentDatatype: ComponentDatatype.FLOAT,
          componentsPerAttribute: 1,
          values: [1.0],
        }),
      },
      indices: [0, 0, 0],
      primitiveType: PrimitiveType.TRIANGLES,
    });

    var geometries = GeometryPipeline.fitToUnsignedShortIndices(geometry);

    expect(geometries.length).toEqual(1);
    expect(geometries[0]).toBe(geometry);
  });

  it("fitToUnsignedShortIndices creates one geometry", function () {
    var sixtyFourK = CesiumMath.SIXTY_FOUR_KILOBYTES;
    var times = [];
    for (var i = 0; i < sixtyFourK + 1; ++i) {
      times.push(i);
    }

    var geometry = new Geometry({
      attributes: {
        time: new GeometryAttribute({
          componentDatatype: ComponentDatatype.FLOAT,
          componentsPerAttribute: 1,
          values: times,
        }),
      },
      indices: [0, 0, 0, sixtyFourK, sixtyFourK, sixtyFourK, 0, sixtyFourK, 0],
      primitiveType: PrimitiveType.TRIANGLES,
    });

    var geometries = GeometryPipeline.fitToUnsignedShortIndices(geometry);

    expect(geometries.length).toEqual(1);
    expect(geometries[0].attributes.time.componentDatatype).toEqual(
      ComponentDatatype.FLOAT
    );
    expect(geometries[0].attributes.time.componentsPerAttribute).toEqual(1);
    expect(geometries[0].attributes.time.values).toEqual([0, sixtyFourK]);

    expect(geometries[0].primitiveType).toEqual(PrimitiveType.TRIANGLES);
    expect(geometries[0].indices).toEqual([0, 0, 0, 1, 1, 1, 0, 1, 0]);
  });

  it("fitToUnsignedShortIndices creates two triangle geometries", function () {
    var sixtyFourK = CesiumMath.SIXTY_FOUR_KILOBYTES;

    var positions = [];
    for (var i = 0; i < sixtyFourK + 1; ++i) {
      positions.push(i, i, i);
    }

    var indices = [];
    for (var j = sixtyFourK; j > 1; j -= 3) {
      indices.push(j, j - 1, j - 2);
    }
    indices.push(0, 1, 2);

    var geometry = new Geometry({
      attributes: {
        position: new GeometryAttribute({
          componentDatatype: ComponentDatatype.FLOAT,
          componentsPerAttribute: 3,
          values: positions,
        }),
      },
      indices: indices,
      primitiveType: PrimitiveType.TRIANGLES,
    });

    var geometries = GeometryPipeline.fitToUnsignedShortIndices(geometry);

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

    expect(geometries[0].attributes.position.values.length).toEqual(
      positions.length - 6
    ); // Two vertices are not copied (0, 1)
    expect(geometries[0].indices.length).toEqual(indices.length - 3); // One triangle is not copied (0, 1, 2)

    expect(geometries[1].attributes.position.values.length).toEqual(9);
    expect(geometries[1].indices.length).toEqual(3);
  });

  it("fitToUnsignedShortIndices creates two line geometries", function () {
    var sixtyFourK = CesiumMath.SIXTY_FOUR_KILOBYTES;

    var positions = [];
    for (var i = 0; i < sixtyFourK + 2; ++i) {
      positions.push(i, i, i);
    }

    var indices = [];
    for (var j = sixtyFourK; j > 1; j -= 2) {
      indices.push(j, j - 1);
    }
    indices.push(0, 1);

    var geometry = new Geometry({
      attributes: {
        position: new GeometryAttribute({
          componentDatatype: ComponentDatatype.FLOAT,
          componentsPerAttribute: 3,
          values: positions,
        }),
      },
      indices: indices,
      primitiveType: PrimitiveType.LINES,
    });

    var geometries = GeometryPipeline.fitToUnsignedShortIndices(geometry);

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

    expect(geometries[0].attributes.position.values.length).toEqual(
      positions.length - 12
    ); // Four vertices are not copied
    expect(geometries[0].indices.length).toEqual(indices.length - 4); // Two lines are not copied

    expect(geometries[1].attributes.position.values.length).toEqual(9);
    expect(geometries[1].indices.length).toEqual(4);
  });

  it("fitToUnsignedShortIndices creates two point geometries", function () {
    var sixtyFourK = CesiumMath.SIXTY_FOUR_KILOBYTES;

    var positions = [];
    var indices = [];
    for (var i = 0; i < sixtyFourK + 1; ++i) {
      positions.push(i, i, i);
      indices.push(i);
    }

    var geometry = new Geometry({
      attributes: {
        position: new GeometryAttribute({
          componentDatatype: ComponentDatatype.FLOAT,
          componentsPerAttribute: 3,
          values: positions,
        }),
      },
      indices: indices,
      primitiveType: PrimitiveType.POINTS,
    });

    var geometries = GeometryPipeline.fitToUnsignedShortIndices(geometry);

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

    expect(geometries[0].attributes.position.values.length).toEqual(
      positions.length - 6
    ); // Two vertices are not copied
    expect(geometries[0].indices.length).toEqual(indices.length - 2); // Two points are not copied

    expect(geometries[1].attributes.position.values.length).toEqual(6);
    expect(geometries[1].indices.length).toEqual(2);
  });

  it("fitToUnsignedShortIndices throws without a geometry", function () {
    expect(function () {
      GeometryPipeline.fitToUnsignedShortIndices(undefined);
    }).toThrowDeveloperError();
  });

  it("fitToUnsignedShortIndices throws without triangles, lines, or points", function () {
    var geometry = new Geometry({
      attributes: {
        time: new GeometryAttribute({
          componentDatatype: ComponentDatatype.FLOAT,
          componentsPerAttribute: 1,
          values: [10.0, 11.0, 12.0],
        }),
      },
      indices: [0, 1, 2],
      primitiveType: PrimitiveType.TRIANGLE_STRIP,
    });

    expect(function () {
      return GeometryPipeline.fitToUnsignedShortIndices(geometry);
    }).toThrowDeveloperError();
  });

  it("fitToUnsignedShortIndices throws with different numbers of attributes", function () {
    var geometry = new Geometry({
      attributes: {
        time: new GeometryAttribute({
          componentDatatype: ComponentDatatype.FLOAT,
          componentsPerAttribute: 1,
          values: [10.0],
        }),
        heat: new GeometryAttribute({
          componentDatatype: ComponentDatatype.FLOAT,
          componentsPerAttribute: 1,
          values: [1.0, 2.0],
        }),
      },
      indices: [0, 0, 0],
      primitiveType: PrimitiveType.TRIANGLES,
    });

    expect(function () {
      return GeometryPipeline.fitToUnsignedShortIndices(geometry);
    }).toThrowDeveloperError();
  });

  it("projectTo2D", function () {
    var p1 = new Cartesian3(100000, 200000, 300000);
    var p2 = new Cartesian3(400000, 500000, 600000);

    var geometry = {};
    geometry.attributes = {};
    geometry.attributes.position = {
      componentDatatype: ComponentDatatype.DOUBLE,
      componentsPerAttribute: 3,
      values: [p1.x, p1.y, p1.z, p2.x, p2.y, p2.z],
    };

    geometry = GeometryPipeline.projectTo2D(
      geometry,
      "position",
      "position3D",
      "position2D"
    );

    var ellipsoid = Ellipsoid.WGS84;
    var projection = new GeographicProjection();
    var projectedP1 = projection.project(ellipsoid.cartesianToCartographic(p1));
    var projectedP2 = projection.project(ellipsoid.cartesianToCartographic(p2));

    expect(geometry.attributes.position2D.values[0]).toEqual(projectedP1.x);
    expect(geometry.attributes.position2D.values[1]).toEqual(projectedP1.y);
    expect(geometry.attributes.position2D.values[2]).toEqual(projectedP1.z);
    expect(geometry.attributes.position2D.values[3]).toEqual(projectedP2.x);
    expect(geometry.attributes.position2D.values[4]).toEqual(projectedP2.y);
    expect(geometry.attributes.position2D.values[5]).toEqual(projectedP2.z);

    expect(geometry.attributes.position3D.values[0]).toEqual(p1.x);
    expect(geometry.attributes.position3D.values[1]).toEqual(p1.y);
    expect(geometry.attributes.position3D.values[2]).toEqual(p1.z);
    expect(geometry.attributes.position3D.values[3]).toEqual(p2.x);
    expect(geometry.attributes.position3D.values[4]).toEqual(p2.y);
    expect(geometry.attributes.position3D.values[5]).toEqual(p2.z);
  });

  it("projectTo2D throws without a geometry", function () {
    expect(function () {
      GeometryPipeline.projectTo2D(undefined);
    }).toThrowDeveloperError();
  });

  it("projectTo2D throws without attributeName", function () {
    expect(function () {
      GeometryPipeline.projectTo2D(
        new Geometry({
          attributes: {
            position: new GeometryAttribute({
              componentDatatype: ComponentDatatype.DOUBLE,
              componentsPerAttribute: 3,
              values: [0.0, 0.0, 0.0],
            }),
          },
          primitiveType: PrimitiveType.POINTS,
        })
      );
    }).toThrowDeveloperError();
  });

  it("projectTo2D throws without attributeName3D", function () {
    expect(function () {
      GeometryPipeline.projectTo2D(
        new Geometry({
          attributes: {
            position: new GeometryAttribute({
              componentDatatype: ComponentDatatype.DOUBLE,
              componentsPerAttribute: 3,
              values: [0.0, 0.0, 0.0],
            }),
          },
          primitiveType: PrimitiveType.POINTS,
        }),
        "position"
      );
    }).toThrowDeveloperError();
  });

  it("projectTo2D throws without attributeName2D", function () {
    expect(function () {
      GeometryPipeline.projectTo2D(
        new Geometry({
          attributes: {
            position: new GeometryAttribute({
              componentDatatype: ComponentDatatype.DOUBLE,
              componentsPerAttribute: 3,
              values: [0.0, 0.0, 0.0],
            }),
          },
          primitiveType: PrimitiveType.POINTS,
        }),
        "position",
        "position3D"
      );
    }).toThrowDeveloperError();
  });

  it("projectTo2D throws without attribute", function () {
    expect(function () {
      GeometryPipeline.projectTo2D(
        new Geometry({
          attributes: {
            normal: new GeometryAttribute({
              componentDatatype: ComponentDatatype.DOUBLE,
              componentsPerAttribute: 3,
              values: [0.0, 0.0, 0.0],
            }),
          },
          primitiveType: PrimitiveType.POINTS,
        }),
        "position",
        "position3D",
        "position2D"
      );
    }).toThrowDeveloperError();
  });

  it("projectTo2D throws without ComponentDatatype.DOUBLE", function () {
    expect(function () {
      var geometry = new Geometry({
        attributes: {
          position: new GeometryAttribute({
            componentDatatype: ComponentDatatype.UNSIGNED_SHORT,
            componentsPerAttribute: 1,
            values: [0.0],
          }),
        },
      });
      GeometryPipeline.projectTo2D(
        geometry,
        "position",
        "position3D",
        "position2D"
      );
    }).toThrowDeveloperError();
  });

  it("projectTo2D throws when trying to project a point close to the origin", function () {
    var geometry = {};
    geometry.attributes = {};
    geometry.attributes.position = {
      componentDatatype: ComponentDatatype.DOUBLE,
      componentsPerAttribute: 3,
      values: [100000.0, 200000.0, 300000.0, 0.0, 0.0, 0.0],
    };

    expect(function () {
      return GeometryPipeline.projectTo2D(
        geometry,
        "position",
        "position3D",
        "position2D"
      );
    }).toThrowDeveloperError();
  });

  it("encodeAttribute encodes positions", function () {
    var c = new Cartesian3(-10000000.0, 0.0, 10000000.0);
    var encoded = EncodedCartesian3.fromCartesian(c);

    var geometry = new Geometry({
      attributes: {
        position: new GeometryAttribute({
          componentDatatype: ComponentDatatype.DOUBLE,
          componentsPerAttribute: 3,
          values: [c.x, c.y, c.z],
        }),
      },
      primitiveType: PrimitiveType.POINTS,
    });
    geometry = GeometryPipeline.encodeAttribute(
      geometry,
      "position",
      "positionHigh",
      "positionLow"
    );

    expect(geometry.attributes.positionHigh).toBeDefined();
    expect(geometry.attributes.positionHigh.values[0]).toEqual(encoded.high.x);
    expect(geometry.attributes.positionHigh.values[1]).toEqual(encoded.high.y);
    expect(geometry.attributes.positionHigh.values[2]).toEqual(encoded.high.z);
    expect(geometry.attributes.positionLow).toBeDefined();
    expect(geometry.attributes.positionLow.values[0]).toEqual(encoded.low.x);
    expect(geometry.attributes.positionLow.values[1]).toEqual(encoded.low.y);
    expect(geometry.attributes.positionLow.values[2]).toEqual(encoded.low.z);
    expect(geometry.attributes.position).not.toBeDefined();
  });

  it("encodeAttribute throws without a geometry", function () {
    expect(function () {
      GeometryPipeline.encodeAttribute(undefined);
    }).toThrowDeveloperError();
  });

  it("encodeAttribute throws without attributeName", function () {
    expect(function () {
      GeometryPipeline.encodeAttribute(
        new Geometry({
          attributes: {
            position: new GeometryAttribute({
              componentDatatype: ComponentDatatype.DOUBLE,
              componentsPerAttribute: 3,
              values: [0.0, 0.0, 0.0],
            }),
          },
          primitiveType: PrimitiveType.POINTS,
        })
      );
    }).toThrowDeveloperError();
  });

  it("encodeAttribute throws without attributeHighName", function () {
    expect(function () {
      GeometryPipeline.encodeAttribute(
        new Geometry({
          attributes: {
            position: new GeometryAttribute({
              componentDatatype: ComponentDatatype.DOUBLE,
              componentsPerAttribute: 3,
              values: [0.0, 0.0, 0.0],
            }),
          },
          primitiveType: PrimitiveType.POINTS,
        }),
        "position"
      );
    }).toThrowDeveloperError();
  });

  it("encodeAttribute throws without attributeLowName", function () {
    expect(function () {
      GeometryPipeline.encodeAttribute(
        new Geometry({
          attributes: {
            position: new GeometryAttribute({
              componentDatatype: ComponentDatatype.DOUBLE,
              componentsPerAttribute: 3,
              values: [0.0, 0.0, 0.0],
            }),
          },
          primitiveType: PrimitiveType.POINTS,
        }),
        "position",
        "positionHigh"
      );
    }).toThrowDeveloperError();
  });

  it("encodeAttribute throws without attribute", function () {
    expect(function () {
      GeometryPipeline.encodeAttribute(
        new Geometry({
          attributes: {
            normal: new GeometryAttribute({
              componentDatatype: ComponentDatatype.DOUBLE,
              componentsPerAttribute: 3,
              values: [0.0, 0.0, 0.0],
            }),
          },
          primitiveType: PrimitiveType.POINTS,
        }),
        "position",
        "positionHigh",
        "positionLow"
      );
    }).toThrowDeveloperError();
  });

  it("encodeAttribute throws without ComponentDatatype.DOUBLE", function () {
    expect(function () {
      var geometry = new Geometry({
        attributes: {
          position: new GeometryAttribute({
            componentDatatype: ComponentDatatype.UNSIGNED_SHORT,
            componentsPerAttribute: 1,
            values: [0.0],
          }),
        },
      });
      GeometryPipeline.encodeAttribute(
        geometry,
        "position",
        "positionHigh",
        "positionLow"
      );
    }).toThrowDeveloperError();
  });

  it("transformToWorldCoordinates", function () {
    var instance = new GeometryInstance({
      geometry: new Geometry({
        attributes: {
          position: new GeometryAttribute({
            componentDatatype: ComponentDatatype.FLOAT,
            componentsPerAttribute: 3,
            values: [0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0],
          }),
          normal: new GeometryAttribute({
            componentDatatype: ComponentDatatype.FLOAT,
            componentsPerAttribute: 3,
            values: [0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0],
          }),
        },
        indices: [0, 1, 2],
        primitiveType: PrimitiveType.TRIANGLES,
        boundingSphere: new BoundingSphere(new Cartesian3(0.5, 0.5, 0.0), 1.0),
      }),
      modelMatrix: new Matrix4(
        0.0,
        0.0,
        1.0,
        0.0,
        1.0,
        0.0,
        0.0,
        0.0,
        0.0,
        1.0,
        0.0,
        0.0,
        0.0,
        0.0,
        0.0,
        1.0
      ),
    });

    var transformed = GeometryPipeline.transformToWorldCoordinates(instance);
    var transformedPositions = [0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0];
    var transformedNormals = [1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0];

    expect(transformed.geometry.attributes.position.values).toEqual(
      transformedPositions
    );
    expect(transformed.geometry.attributes.normal.values).toEqual(
      transformedNormals
    );
    expect(transformed.geometry.boundingSphere).toEqual(
      new BoundingSphere(new Cartesian3(0.0, 0.5, 0.5), 1.0)
    );
    expect(transformed.modelMatrix).toEqual(Matrix4.IDENTITY);
  });

  it("transformToWorldCoordinates with non-uniform scale", function () {
    var instance = new GeometryInstance({
      geometry: new Geometry({
        attributes: {
          position: new GeometryAttribute({
            componentDatatype: ComponentDatatype.FLOAT,
            componentsPerAttribute: 3,
            values: [0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0],
          }),
          normal: new GeometryAttribute({
            componentDatatype: ComponentDatatype.FLOAT,
            componentsPerAttribute: 3,
            values: [0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0],
          }),
        },
        indices: [0, 1, 2],
        primitiveType: PrimitiveType.TRIANGLES,
        boundingSphere: new BoundingSphere(new Cartesian3(0.5, 0.5, 0.0), 1.0),
      }),
      modelMatrix: Matrix4.fromScale(new Cartesian3(1.0, 2.0, 4.0)),
    });

    var transformed = GeometryPipeline.transformToWorldCoordinates(instance);
    var transformedPositions = [0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 2.0, 0.0];
    var transformedNormals = [0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0];

    expect(transformed.geometry.attributes.position.values).toEqual(
      transformedPositions
    );
    expect(transformed.geometry.attributes.normal.values).toEqual(
      transformedNormals
    );
    expect(transformed.geometry.boundingSphere).toEqual(
      new BoundingSphere(new Cartesian3(0.5, 1.0, 0.0), 4.0)
    );
    expect(transformed.modelMatrix).toEqual(Matrix4.IDENTITY);
  });

  it("transformToWorldCoordinates does nothing when already in world coordinates", function () {
    var instance = new GeometryInstance({
      geometry: new Geometry({
        attributes: {
          position: new GeometryAttribute({
            componentDatatype: ComponentDatatype.FLOAT,
            componentsPerAttribute: 3,
            values: [0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0],
          }),
          normal: new GeometryAttribute({
            componentDatatype: ComponentDatatype.FLOAT,
            componentsPerAttribute: 3,
            values: [0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0],
          }),
        },
        indices: [0, 1, 2],
        primitiveType: PrimitiveType.TRIANGLES,
        boundingSphere: new BoundingSphere(new Cartesian3(0.5, 0.5, 0.0), 1.0),
      }),
      modelMatrix: Matrix4.IDENTITY,
    });

    var transformed = GeometryPipeline.transformToWorldCoordinates(instance);
    var transformedPositions = [0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0];
    var transformedNormals = [0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0];

    expect(transformed.geometry.attributes.position.values).toEqual(
      transformedPositions
    );
    expect(transformed.geometry.attributes.normal.values).toEqual(
      transformedNormals
    );
    expect(transformed.geometry.boundingSphere).toEqual(
      new BoundingSphere(new Cartesian3(0.5, 0.5, 0.0), 1.0)
    );
    expect(transformed.modelMatrix).toEqual(Matrix4.IDENTITY);
  });

  it("transformToWorldCoordinates throws without an instance", function () {
    expect(function () {
      GeometryPipeline.transformToWorldCoordinates();
    }).toThrowDeveloperError();
  });

  it("combineInstances combines one geometry", function () {
    var instance = new GeometryInstance({
      geometry: new Geometry({
        attributes: {
          position: new GeometryAttribute({
            componentDatatype: ComponentDatatype.FLOAT,
            componentsPerAttribute: 3,
            values: new Float32Array([0.0, 0.0, 0.0]),
          }),
        },
        primitiveType: PrimitiveType.POINTS,
      }),
    });

    var combined = GeometryPipeline.combineInstances([instance])[0];
    expect(combined).toEqual(instance.geometry);
  });

  it("combineInstances combines several geometries without indicess", function () {
    var instance = new GeometryInstance({
      geometry: new Geometry({
        attributes: {
          position: new GeometryAttribute({
            componentDatatype: ComponentDatatype.FLOAT,
            componentsPerAttribute: 3,
            values: [0.0, 0.0, 0.0],
          }),
        },
        primitiveType: PrimitiveType.POINTS,
      }),
    });
    var anotherInstance = new GeometryInstance({
      geometry: new Geometry({
        attributes: {
          position: new GeometryAttribute({
            componentDatatype: ComponentDatatype.FLOAT,
            componentsPerAttribute: 3,
            values: [1.0, 1.0, 1.0],
          }),
        },
        primitiveType: PrimitiveType.POINTS,
      }),
    });

    var combined = GeometryPipeline.combineInstances([
      instance,
      anotherInstance,
    ])[0];
    expect(combined).toEqual(
      new Geometry({
        attributes: {
          position: new GeometryAttribute({
            componentDatatype: ComponentDatatype.FLOAT,
            componentsPerAttribute: 3,
            values: new Float32Array([0.0, 0.0, 0.0, 1.0, 1.0, 1.0]),
          }),
        },
        primitiveType: PrimitiveType.POINTS,
      })
    );
  });

  it("combineInstances combines several geometries with indicess", function () {
    var instance = new GeometryInstance({
      geometry: new Geometry({
        attributes: {
          position: new GeometryAttribute({
            componentDatatype: ComponentDatatype.FLOAT,
            componentsPerAttribute: 3,
            values: [0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 2.0, 2.0, 2.0],
          }),
          normal: new GeometryAttribute({
            componentDatatype: ComponentDatatype.FLOAT,
            componentsPerAttribute: 3,
            values: [0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 2.0, 2.0, 2.0],
          }),
        },
        indices: [0, 1, 2],
        primitiveType: PrimitiveType.TRIANGLES,
      }),
    });
    var anotherInstance = new GeometryInstance({
      geometry: new Geometry({
        attributes: {
          position: new GeometryAttribute({
            componentDatatype: ComponentDatatype.FLOAT,
            componentsPerAttribute: 3,
            values: [3.0, 3.0, 3.0, 4.0, 4.0, 4.0, 5.0, 5.0, 5.0],
          }),
        },
        indices: [0, 1, 2],
        primitiveType: PrimitiveType.TRIANGLES,
      }),
    });

    var combined = GeometryPipeline.combineInstances([
      instance,
      anotherInstance,
    ])[0];
    expect(combined).toEqual(
      new Geometry({
        attributes: {
          position: new GeometryAttribute({
            componentDatatype: ComponentDatatype.FLOAT,
            componentsPerAttribute: 3,
            values: new Float32Array([
              0.0,
              0.0,
              0.0,
              1.0,
              1.0,
              1.0,
              2.0,
              2.0,
              2.0,
              3.0,
              3.0,
              3.0,
              4.0,
              4.0,
              4.0,
              5.0,
              5.0,
              5.0,
            ]),
          }),
        },
        indices: new Uint16Array([0, 1, 2, 3, 4, 5]),
        primitiveType: PrimitiveType.TRIANGLES,
      })
    );
  });

  it("combineInstances with geometry that is and is not split by the IDL", function () {
    var instances = [
      GeometryPipeline.splitLongitude(
        new GeometryInstance({
          geometry: PolygonGeometry.createGeometry(
            PolygonGeometry.fromPositions({
              positions: Cartesian3.fromDegreesArray([
                179.0,
                1.0,
                179.0,
                -1.0,
                -179.0,
                -1.0,
                -179.0,
                1.0,
              ]),
              vertexFormat: VertexFormat.POSITION_ONLY,
              granularity: 2.0 * CesiumMath.RADIANS_PER_DEGREE,
            })
          ),
        })
      ),
      new GeometryInstance({
        geometry: PolygonGeometry.createGeometry(
          PolygonGeometry.fromPositions({
            positions: Cartesian3.fromDegreesArray([
              -1.0,
              1.0,
              -1.0,
              -1.0,
              1.0,
              -1.0,
              1.0,
              1.0,
            ]),
            vertexFormat: VertexFormat.POSITION_ONLY,
            granularity: 2.0 * CesiumMath.RADIANS_PER_DEGREE,
          })
        ),
      }),
    ];

    var combinedInstances = GeometryPipeline.combineInstances(instances);
    expect(combinedInstances.length).toEqual(3);
  });

  it("combineInstances combines bounding spheres", function () {
    var instance = new GeometryInstance({
      geometry: new Geometry({
        attributes: {
          position: new GeometryAttribute({
            componentDatatype: ComponentDatatype.FLOAT,
            componentsPerAttribute: 3,
            values: [0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0],
          }),
        },
        indices: [0, 1, 2],
        primitiveType: PrimitiveType.TRIANGLES,
        boundingSphere: new BoundingSphere(new Cartesian3(0.5, 0.5, 0.0), 1.0),
      }),
    });
    var anotherInstance = new GeometryInstance({
      geometry: new Geometry({
        attributes: {
          position: new GeometryAttribute({
            componentDatatype: ComponentDatatype.FLOAT,
            componentsPerAttribute: 3,
            values: [1.0, 0.0, 0.0, 2.0, 0.0, 0.0, 1.0, 1.0, 0.0],
          }),
        },
        indices: [0, 1, 2],
        primitiveType: PrimitiveType.TRIANGLES,
        boundingSphere: new BoundingSphere(new Cartesian3(1.5, 0.5, 0.0), 1.0),
      }),
    });

    var combined = GeometryPipeline.combineInstances([
      instance,
      anotherInstance,
    ])[0];
    var expected = BoundingSphere.union(
      instance.geometry.boundingSphere,
      anotherInstance.geometry.boundingSphere
    );
    expect(combined.boundingSphere).toEqual(expected);
  });

  it("combineInstances throws without instances", function () {
    expect(function () {
      GeometryPipeline.combineInstances();
    }).toThrowDeveloperError();
  });

  it("combineInstances throws when instances.length is zero", function () {
    expect(function () {
      GeometryPipeline.combineInstances([]);
    }).toThrowDeveloperError();
  });

  it("combineInstances throws when instances.modelMatrix do not match", function () {
    var instance0 = new GeometryInstance({
      geometry: new Geometry({
        attributes: {
          position: new GeometryAttribute({
            componentDatatype: ComponentDatatype.FLOAT,
            componentsPerAttribute: 3,
            values: [0.0, 0.0, 0.0],
          }),
        },
        primitiveType: PrimitiveType.POINTS,
      }),
      modelMatrix: Matrix4.fromScale(new Cartesian3(1.0, 1.0, 1.0)),
    });

    var instance1 = new GeometryInstance({
      geometry: new Geometry({
        attributes: {
          position: new GeometryAttribute({
            componentDatatype: ComponentDatatype.FLOAT,
            componentsPerAttribute: 3,
            values: [0.0, 0.0, 0.0],
          }),
        },
        primitiveType: PrimitiveType.POINTS,
      }),
      modelMatrix: Matrix4.fromScale(new Cartesian3(2.0, 2.0, 2.0)),
    });

    expect(function () {
      GeometryPipeline.combineInstances([instance0, instance1]);
    }).toThrowDeveloperError();
  });

  it("combineInstances throws when instance geometries do not all have or not have an indices", function () {
    var instance0 = new GeometryInstance({
      geometry: new Geometry({
        attributes: {
          position: new GeometryAttribute({
            componentDatatype: ComponentDatatype.FLOAT,
            componentsPerAttribute: 3,
            values: [0.0, 0.0, 0.0],
          }),
        },
        indices: [0],
        primitiveType: PrimitiveType.POINTS,
      }),
    });

    var instance1 = new GeometryInstance({
      geometry: new Geometry({
        attributes: {
          position: new GeometryAttribute({
            componentDatatype: ComponentDatatype.FLOAT,
            componentsPerAttribute: 3,
            values: [0.0, 0.0, 0.0],
          }),
        },
        primitiveType: PrimitiveType.POINTS,
      }),
    });

    expect(function () {
      GeometryPipeline.combineInstances([instance0, instance1]);
    }).toThrowDeveloperError();
  });

  it("combineInstances throws when instance geometries do not all have the same primitive type", function () {
    var instance0 = new GeometryInstance({
      geometry: new Geometry({
        attributes: {
          position: new GeometryAttribute({
            componentDatatype: ComponentDatatype.FLOAT,
            componentsPerAttribute: 3,
            values: [0.0, 0.0, 0.0],
          }),
        },
        primitiveType: PrimitiveType.POINTS,
      }),
    });

    var instance1 = new GeometryInstance({
      geometry: new Geometry({
        attributes: {
          position: new GeometryAttribute({
            componentDatatype: ComponentDatatype.FLOAT,
            componentsPerAttribute: 3,
            values: [0.0, 0.0, 0.0, 1.0, 0.0, 0.0],
          }),
        },
        primitiveType: PrimitiveType.LINES,
      }),
    });

    expect(function () {
      GeometryPipeline.combineInstances([instance0, instance1]);
    }).toThrowDeveloperError();
  });

  it("computeNormal throws when geometry is undefined", function () {
    expect(function () {
      GeometryPipeline.computeNormal();
    }).toThrowDeveloperError();
  });

  it("computeNormal throws when geometry.attributes.position is undefined", function () {
    var geometry = new Geometry({
      attributes: {},
      primitiveType: PrimitiveType.TRIANGLES,
    });

    expect(function () {
      GeometryPipeline.computeNormal(geometry);
    }).toThrowDeveloperError();
  });

  it("computeNormal throws when geometry.indices is undefined", function () {
    var geometry = new Geometry({
      attributes: {
        position: new GeometryAttribute({
          values: [0, 0, 0, 1, 0, 0, 0, 1, 0],
          componentsPerAttribute: 3,
          componentDatatype: ComponentDatatype.FLOAT,
        }),
      },
      primitiveType: PrimitiveType.TRIANGLES,
    });

    expect(function () {
      GeometryPipeline.computeNormal(geometry);
    }).toThrowDeveloperError();
  });

  it("computeNormal throws when geometry.indices.length is not a multiple of 3", function () {
    var geometry = new Geometry({
      attributes: {
        position: new GeometryAttribute({
          values: [0, 0, 0, 1, 0, 0, 0, 1],
          componentsPerAttribute: 3,
          componentDatatype: ComponentDatatype.FLOAT,
        }),
      },
      primitiveType: PrimitiveType.TRIANGLES,
    });

    expect(function () {
      GeometryPipeline.computeNormal(geometry);
    }).toThrowDeveloperError();
  });

  it("computeNormal throws when primitive type is not triangle", function () {
    var geometry = new Geometry({
      attributes: {
        position: new GeometryAttribute({
          values: [0, 0, 0, 1, 0, 0, 0, 1, 0],
          componentsPerAttribute: 3,
          componentDatatype: ComponentDatatype.FLOAT,
        }),
      },
      indices: [0, 1, 2],
      primitiveType: PrimitiveType.TRIANGLE_STRIP,
    });

    expect(function () {
      GeometryPipeline.computeNormal(geometry);
    }).toThrowDeveloperError();
  });

  it("computeNormal computes normal for one triangle", function () {
    var geometry = new Geometry({
      attributes: {
        position: new GeometryAttribute({
          values: [0, 0, 0, 1, 0, 0, 0, 1, 0],
          componentsPerAttribute: 3,
          componentDatatype: ComponentDatatype.FLOAT,
        }),
      },
      indices: [0, 1, 2],
      primitiveType: PrimitiveType.TRIANGLES,
    });

    geometry = GeometryPipeline.computeNormal(geometry);

    expect(geometry.attributes.normal.values.length).toEqual(3 * 3);
    expect(geometry.attributes.normal.values).toEqual([
      0,
      0,
      1,
      0,
      0,
      1,
      0,
      0,
      1,
    ]);
  });

  it("computeNormal computes normal for two triangles", function () {
    var geometry = new Geometry({
      attributes: {
        position: new GeometryAttribute({
          values: [0, 0, 0, 1, 0, 1, 1, 1, 1, 2, 0, 0],
          componentsPerAttribute: 3,
          componentDatatype: ComponentDatatype.FLOAT,
        }),
      },
      indices: [0, 1, 2, 1, 3, 2],
      primitiveType: PrimitiveType.TRIANGLES,
    });

    geometry = GeometryPipeline.computeNormal(geometry);

    var normals = geometry.attributes.normal.values;
    expect(normals.length).toEqual(4 * 3);

    var a = Cartesian3.normalize(new Cartesian3(-1, 0, 1), new Cartesian3());

    expect(Cartesian3.fromArray(normals, 0)).toEqualEpsilon(
      a,
      CesiumMath.EPSILON7
    );
    expect(Cartesian3.fromArray(normals, 3)).toEqualEpsilon(
      Cartesian3.UNIT_Z,
      CesiumMath.EPSILON7
    );
    expect(Cartesian3.fromArray(normals, 6)).toEqualEpsilon(
      Cartesian3.UNIT_Z,
      CesiumMath.EPSILON7
    );

    a = Cartesian3.normalize(new Cartesian3(1, 0, 1), new Cartesian3());
    expect(Cartesian3.fromArray(normals, 9)).toEqualEpsilon(
      a,
      CesiumMath.EPSILON7
    );
  });

  it("computeNormal computes normal for six triangles", function () {
    var geometry = new Geometry({
      attributes: {
        position: new GeometryAttribute({
          values: [
            0,
            0,
            0,
            1,
            0,
            0,
            1,
            0,
            1,
            0,
            0,
            1,
            0,
            1,
            1,
            0,
            1,
            0,
            1,
            1,
            0,
          ],
          componentsPerAttribute: 3,
          componentDatatype: ComponentDatatype.FLOAT,
        }),
      },
      indices: [0, 1, 2, 3, 0, 2, 4, 0, 3, 4, 5, 0, 5, 6, 0, 6, 1, 0],
      primitiveType: PrimitiveType.TRIANGLES,
    });

    geometry = GeometryPipeline.computeNormal(geometry);

    var normals = geometry.attributes.normal.values;
    expect(normals.length).toEqual(7 * 3);

    var a = Cartesian3.normalize(new Cartesian3(-1, -1, -1), new Cartesian3());
    expect(Cartesian3.fromArray(normals, 0)).toEqualEpsilon(
      a,
      CesiumMath.EPSILON7
    );

    a = Cartesian3.normalize(new Cartesian3(0, -1, -1), new Cartesian3());
    expect(Cartesian3.fromArray(normals, 3)).toEqualEpsilon(
      a,
      CesiumMath.EPSILON7
    );

    expect(Cartesian3.fromArray(normals, 6)).toEqualEpsilon(
      Cartesian3.negate(Cartesian3.UNIT_Y, new Cartesian3()),
      CesiumMath.EPSILON7
    );

    a = Cartesian3.normalize(new Cartesian3(-1, -1, 0), new Cartesian3());
    expect(Cartesian3.fromArray(normals, 9)).toEqualEpsilon(
      a,
      CesiumMath.EPSILON7
    );

    expect(Cartesian3.fromArray(normals, 12)).toEqualEpsilon(
      Cartesian3.negate(Cartesian3.UNIT_X, new Cartesian3()),
      CesiumMath.EPSILON7
    );

    a = Cartesian3.normalize(new Cartesian3(-1, 0, -1), new Cartesian3());
    expect(Cartesian3.fromArray(normals, 15)).toEqualEpsilon(
      a,
      CesiumMath.EPSILON7
    );

    expect(Cartesian3.fromArray(normals, 18)).toEqualEpsilon(
      Cartesian3.negate(Cartesian3.UNIT_Z, new Cartesian3()),
      CesiumMath.EPSILON7
    );
  });

  it("computeNormal computes normal of (0,0,1) for a degenerate triangle", function () {
    var geometry = new Geometry({
      attributes: {
        position: new GeometryAttribute({
          values: [0, 0, 0, 1, 0, 0],
          componentsPerAttribute: 3,
          componentDatatype: ComponentDatatype.FLOAT,
        }),
      },
      indices: [0, 1, 0],
      primitiveType: PrimitiveType.TRIANGLES,
    });

    geometry = GeometryPipeline.computeNormal(geometry);

    expect(geometry.attributes.normal.values.length).toEqual(2 * 3);
    expect(geometry.attributes.normal.values).toEqual([0, 0, 1, 0, 0, 1]);
  });

  it("computeNormal takes first normal for two coplanar triangles with opposite winding orders", function () {
    var geometry = new Geometry({
      attributes: {
        position: new GeometryAttribute({
          values: [0, 0, 0, 1, 0, 1, 1, 1, 1],
          componentsPerAttribute: 3,
          componentDatatype: ComponentDatatype.FLOAT,
        }),
      },
      indices: [0, 1, 2, 2, 1, 0],
      primitiveType: PrimitiveType.TRIANGLES,
    });

    geometry = GeometryPipeline.computeNormal(geometry);

    var normals = geometry.attributes.normal.values;
    expect(normals.length).toEqual(3 * 3);

    var a = Cartesian3.normalize(new Cartesian3(-1, 0, 1), new Cartesian3());

    expect(Cartesian3.fromArray(normals, 0)).toEqualEpsilon(
      a,
      CesiumMath.EPSILON7
    );
    expect(Cartesian3.fromArray(normals, 3)).toEqualEpsilon(
      a,
      CesiumMath.EPSILON7
    );
    expect(Cartesian3.fromArray(normals, 6)).toEqualEpsilon(
      a,
      CesiumMath.EPSILON7
    );
  });

  it("computeTangentAndBitangent throws when geometry is undefined", function () {
    expect(function () {
      GeometryPipeline.computeTangentAndBitangent();
    }).toThrowDeveloperError();
  });

  it("computeTangentAndBitangent throws when position is undefined", function () {
    var geometry = new Geometry({
      attributes: {
        normal: new GeometryAttribute({
          values: [0, 0, 0, 1, 0, 0, 0, 1, 0],
          componentsPerAttribute: 3,
          componentDatatype: ComponentDatatype.FLOAT,
        }),
        st: new GeometryAttribute({
          values: [0, 0, 1, 1],
          componentsPerAttribute: 2,
          componentDatatype: ComponentDatatype.FLOAT,
        }),
      },
      indices: [0, 1, 2],
      primitiveType: PrimitiveType.TRIANGLE_STRIP,
    });

    expect(function () {
      GeometryPipeline.computeTangentAndBitangent(geometry);
    }).toThrowDeveloperError();
  });

  it("computeTangentAndBitangent throws when normal is undefined", function () {
    var geometry = new Geometry({
      attributes: {
        position: new GeometryAttribute({
          values: [0, 0, 0, 1, 0, 0, 0, 1, 0],
          componentsPerAttribute: 3,
          componentDatatype: ComponentDatatype.FLOAT,
        }),
        st: new GeometryAttribute({
          values: [0, 0, 1, 1],
          componentsPerAttribute: 2,
          componentDatatype: ComponentDatatype.FLOAT,
        }),
      },
      indices: [0, 1, 2],
      primitiveType: PrimitiveType.TRIANGLE_STRIP,
    });

    expect(function () {
      GeometryPipeline.computeTangentAndBitangent(geometry);
    }).toThrowDeveloperError();
  });

  it("computeTangentAndBitangent throws when st is undefined", function () {
    var geometry = new Geometry({
      attributes: {
        position: new GeometryAttribute({
          values: [0, 0, 0, 1, 0, 0, 0, 1, 0],
          componentsPerAttribute: 3,
          componentDatatype: ComponentDatatype.FLOAT,
        }),
        normal: new GeometryAttribute({
          values: [0, 0, 0, 1, 0, 0, 0, 1, 0],
          componentsPerAttribute: 3,
          componentDatatype: ComponentDatatype.FLOAT,
        }),
      },
      indices: [0, 1, 2],
      primitiveType: PrimitiveType.TRIANGLE_STRIP,
    });

    expect(function () {
      GeometryPipeline.computeTangentAndBitangent(geometry);
    }).toThrowDeveloperError();
  });

  it("computeTangentAndBitangent throws when geometry.indices is undefined", function () {
    var geometry = new Geometry({
      attributes: {
        position: new GeometryAttribute({
          values: [0, 0, 0, 1, 0, 0, 0, 1, 0],
          componentsPerAttribute: 3,
          componentDatatype: ComponentDatatype.FLOAT,
        }),
        normal: new GeometryAttribute({
          values: [0, 0, 0, 1, 0, 0, 0, 1, 0],
          componentsPerAttribute: 3,
          componentDatatype: ComponentDatatype.FLOAT,
        }),
        st: new GeometryAttribute({
          values: [0, 0, 1, 1],
          componentsPerAttribute: 2,
          componentDatatype: ComponentDatatype.FLOAT,
        }),
      },
      primitiveType: PrimitiveType.POINTS,
    });

    expect(function () {
      GeometryPipeline.computeTangentAndBitangent(geometry);
    }).toThrowDeveloperError();
  });

  it("computeTangentAndBitangent throws when indices is not a multiple of 3", function () {
    var geometry = new Geometry({
      attributes: {
        position: new GeometryAttribute({
          values: [0, 0, 0, 1, 0, 0, 0, 1, 0],
          componentsPerAttribute: 3,
          componentDatatype: ComponentDatatype.FLOAT,
        }),
        normal: new GeometryAttribute({
          values: [0, 0, 0, 1, 0, 0, 0, 1, 0],
          componentsPerAttribute: 3,
          componentDatatype: ComponentDatatype.FLOAT,
        }),
        st: new GeometryAttribute({
          values: [0, 0, 1, 1],
          componentsPerAttribute: 2,
          componentDatatype: ComponentDatatype.FLOAT,
        }),
      },
      indices: [0, 1, 2, 3, 4],
      primitiveType: PrimitiveType.TRIANGLES,
    });

    expect(function () {
      GeometryPipeline.computeTangentAndBitangent(geometry);
    }).toThrowDeveloperError();
  });

  it("computeTangentAndBitangent throws when primitive type is not triangle", function () {
    var geometry = new Geometry({
      attributes: {
        position: new GeometryAttribute({
          values: [0, 0, 0, 1, 0, 0, 0, 1, 0],
          componentsPerAttribute: 3,
          componentDatatype: ComponentDatatype.FLOAT,
        }),
        normal: new GeometryAttribute({
          values: [0, 0, 0, 1, 0, 0, 0, 1, 0],
          componentsPerAttribute: 3,
          componentDatatype: ComponentDatatype.FLOAT,
        }),
        st: new GeometryAttribute({
          values: [0, 0, 1, 1],
          componentsPerAttribute: 2,
          componentDatatype: ComponentDatatype.FLOAT,
        }),
      },
      indices: [0, 1, 2],
      primitiveType: PrimitiveType.TRIANGLE_STRIP,
    });

    expect(function () {
      GeometryPipeline.computeTangentAndBitangent(geometry);
    }).toThrowDeveloperError();
  });

  it("computeTangentAndBitangent computes tangent and bitangent for one triangle", function () {
    var geometry = new Geometry({
      attributes: {
        position: new GeometryAttribute({
          values: [0, 0, 0, 1, 0, 0, 0, 1, 0],
          componentsPerAttribute: 3,
          componentDatatype: ComponentDatatype.FLOAT,
        }),
        st: new GeometryAttribute({
          values: [0, 0, 1, 0, 0, 1],
          componentsPerAttribute: 2,
          componentDatatype: ComponentDatatype.FLOAT,
        }),
      },
      indices: [0, 1, 2],
      primitiveType: PrimitiveType.TRIANGLES,
    });

    geometry = GeometryPipeline.computeNormal(geometry);
    geometry = GeometryPipeline.computeTangentAndBitangent(geometry);

    expect(geometry.attributes.tangent.values).toEqual([
      1,
      0,
      0,
      1,
      0,
      0,
      1,
      0,
      0,
    ]);
    expect(geometry.attributes.bitangent.values).toEqual([
      0,
      1,
      0,
      0,
      1,
      0,
      0,
      1,
      0,
    ]);
  });

  it("computeTangentAndBitangent computes tangent and bitangent for two triangles", function () {
    var geometry = new Geometry({
      attributes: {
        position: new GeometryAttribute({
          values: [0, 0, 0, 1, 0, 1, 1, 1, 1, 2, 0, 0],
          componentsPerAttribute: 3,
          componentDatatype: ComponentDatatype.FLOAT,
        }),
        st: new GeometryAttribute({
          values: [0, 0, 1, 0, 1, 1, 0, 1],
          componentsPerAttribute: 2,
          componentDatatype: ComponentDatatype.FLOAT,
        }),
      },
      indices: [0, 1, 2, 1, 3, 2],
      primitiveType: PrimitiveType.TRIANGLES,
    });

    geometry = GeometryPipeline.computeNormal(geometry);
    geometry = GeometryPipeline.computeTangentAndBitangent(geometry);
    expect(geometry.attributes.tangent.values).toEqualEpsilon(
      [
        0.7071067811865475,
        0,
        0.7071067811865475,
        0,
        1,
        0,
        0,
        1,
        0,
        -0.5773502691896258,
        0.5773502691896258,
        0.5773502691896258,
      ],
      CesiumMath.EPSILON7
    );
    expect(geometry.attributes.bitangent.values).toEqualEpsilon(
      [
        0,
        1,
        0,
        -1,
        0,
        0,
        -1,
        0,
        0,
        -0.4082482904638631,
        -0.8164965809277261,
        0.4082482904638631,
      ],
      CesiumMath.EPSILON7
    );
  });

  it("computeTangentAndBitangent computes tangent and bitangent for BoxGeometry", function () {
    var geometry = BoxGeometry.createGeometry(
      new BoxGeometry({
        vertexFormat: new VertexFormat({
          position: true,
          normal: true,
          st: true,
        }),
        maximum: new Cartesian3(250000.0, 250000.0, 250000.0),
        minimum: new Cartesian3(-250000.0, -250000.0, -250000.0),
      })
    );
    geometry = GeometryPipeline.computeTangentAndBitangent(geometry);
    var actualTangents = geometry.attributes.tangent.values;
    var actualBitangents = geometry.attributes.bitangent.values;

    var expectedGeometry = BoxGeometry.createGeometry(
      new BoxGeometry({
        vertexFormat: VertexFormat.ALL,
        maximum: new Cartesian3(250000.0, 250000.0, 250000.0),
        minimum: new Cartesian3(-250000.0, -250000.0, -250000.0),
      })
    );
    var expectedTangents = expectedGeometry.attributes.tangent.values;
    var expectedBitangents = expectedGeometry.attributes.bitangent.values;

    expect(actualTangents.length).toEqual(expectedTangents.length);
    expect(actualBitangents.length).toEqual(expectedBitangents.length);

    for (var i = 0; i < actualTangents.length; i += 3) {
      var actual = Cartesian3.fromArray(actualTangents, i);
      var expected = Cartesian3.fromArray(expectedTangents, i);
      expect(actual).toEqualEpsilon(expected, CesiumMath.EPSILON1);

      actual = Cartesian3.fromArray(actualBitangents, i);
      expected = Cartesian3.fromArray(expectedBitangents, i);
      expect(actual).toEqualEpsilon(expected, CesiumMath.EPSILON1);
    }
  });

  it("compressVertices throws without geometry", function () {
    expect(function () {
      return GeometryPipeline.compressVertices();
    }).toThrowDeveloperError();
  });

  it("compressVertices on geometry without normals or texture coordinates does nothing", function () {
    var geometry = BoxGeometry.createGeometry(
      new BoxGeometry({
        vertexFormat: new VertexFormat({
          position: true,
        }),
        maximum: new Cartesian3(250000.0, 250000.0, 250000.0),
        minimum: new Cartesian3(-250000.0, -250000.0, -250000.0),
      })
    );
    expect(geometry.attributes.normal).not.toBeDefined();
    geometry = GeometryPipeline.compressVertices(geometry);
    expect(geometry.attributes.normal).not.toBeDefined();
  });

  it("compressVertices compresses normals", function () {
    var geometry = BoxGeometry.createGeometry(
      new BoxGeometry({
        vertexFormat: new VertexFormat({
          position: true,
          normal: true,
        }),
        maximum: new Cartesian3(250000.0, 250000.0, 250000.0),
        minimum: new Cartesian3(-250000.0, -250000.0, -250000.0),
      })
    );
    expect(geometry.attributes.normal).toBeDefined();
    var originalNormals = arraySlice(geometry.attributes.normal.values);

    geometry = GeometryPipeline.compressVertices(geometry);

    expect(geometry.attributes.compressedAttributes).toBeDefined();

    var normals = geometry.attributes.compressedAttributes.values;
    expect(normals.length).toEqual(originalNormals.length / 3);

    for (var i = 0; i < normals.length; ++i) {
      expect(
        AttributeCompression.octDecodeFloat(normals[i], new Cartesian3())
      ).toEqualEpsilon(
        Cartesian3.fromArray(originalNormals, i * 3),
        CesiumMath.EPSILON2
      );
    }
  });

  it("compressVertices compresses texture coordinates", function () {
    var geometry = BoxGeometry.createGeometry(
      new BoxGeometry({
        vertexFormat: new VertexFormat({
          position: true,
          st: true,
        }),
        maximum: new Cartesian3(250000.0, 250000.0, 250000.0),
        minimum: new Cartesian3(-250000.0, -250000.0, -250000.0),
      })
    );
    expect(geometry.attributes.st).toBeDefined();
    var originalST = arraySlice(geometry.attributes.st.values);

    geometry = GeometryPipeline.compressVertices(geometry);

    expect(geometry.attributes.st).not.toBeDefined();
    expect(geometry.attributes.compressedAttributes).toBeDefined();

    var st = geometry.attributes.compressedAttributes.values;
    expect(st.length).toEqual(originalST.length / 2);

    for (var i = 0; i < st.length; ++i) {
      var temp = st[i] / 4096.0;
      var stx = Math.floor(temp) / 4096.0;
      var sty = temp - Math.floor(temp);
      var texCoord = new Cartesian2(stx, sty);
      expect(texCoord).toEqualEpsilon(
        Cartesian2.fromArray(originalST, i * 2, new Cartesian2()),
        CesiumMath.EPSILON2
      );
    }
  });

  it("compressVertices packs compressed normals with texture coordinates", function () {
    var geometry = BoxGeometry.createGeometry(
      new BoxGeometry({
        vertexFormat: new VertexFormat({
          position: true,
          normal: true,
          st: true,
        }),
        maximum: new Cartesian3(250000.0, 250000.0, 250000.0),
        minimum: new Cartesian3(-250000.0, -250000.0, -250000.0),
      })
    );
    expect(geometry.attributes.normal).toBeDefined();
    expect(geometry.attributes.st).toBeDefined();
    var originalNormals = arraySlice(geometry.attributes.normal.values);
    var originalST = arraySlice(geometry.attributes.st.values);

    geometry = GeometryPipeline.compressVertices(geometry);

    expect(geometry.attributes.normal).not.toBeDefined();
    expect(geometry.attributes.st).not.toBeDefined();
    expect(geometry.attributes.compressedAttributes).toBeDefined();

    var stNormal = geometry.attributes.compressedAttributes.values;
    expect(stNormal.length).toEqual(originalST.length);

    for (var i = 0; i < stNormal.length; i += 2) {
      expect(
        AttributeCompression.decompressTextureCoordinates(
          stNormal[i],
          new Cartesian2()
        )
      ).toEqualEpsilon(
        Cartesian2.fromArray(originalST, i, new Cartesian2()),
        CesiumMath.EPSILON2
      );
      expect(
        AttributeCompression.octDecodeFloat(stNormal[i + 1], new Cartesian3())
      ).toEqualEpsilon(
        Cartesian3.fromArray(originalNormals, (i / 2) * 3),
        CesiumMath.EPSILON2
      );
    }
  });

  it("compressVertices packs compressed tangents and bitangents", function () {
    var geometry = BoxGeometry.createGeometry(
      new BoxGeometry({
        vertexFormat: new VertexFormat({
          position: true,
          normal: true,
          tangent: true,
          bitangent: true,
        }),
        maximum: new Cartesian3(250000.0, 250000.0, 250000.0),
        minimum: new Cartesian3(-250000.0, -250000.0, -250000.0),
      })
    );
    expect(geometry.attributes.normal).toBeDefined();
    expect(geometry.attributes.tangent).toBeDefined();
    expect(geometry.attributes.bitangent).toBeDefined();
    var originalNormals = arraySlice(geometry.attributes.normal.values);
    var originalTangents = arraySlice(geometry.attributes.tangent.values);
    var originalBitangents = arraySlice(geometry.attributes.bitangent.values);

    geometry = GeometryPipeline.compressVertices(geometry);

    expect(geometry.attributes.tangent).not.toBeDefined();
    expect(geometry.attributes.bitangent).not.toBeDefined();
    expect(geometry.attributes.compressedAttributes).toBeDefined();

    var compressedNormals = geometry.attributes.compressedAttributes.values;
    expect(compressedNormals.length).toEqual((originalNormals.length / 3) * 2);

    var normal = new Cartesian3();
    var tangent = new Cartesian3();
    var bitangent = new Cartesian3();

    for (var i = 0; i < compressedNormals.length; i += 2) {
      var compressed = Cartesian2.fromArray(
        compressedNormals,
        i,
        new Cartesian2()
      );
      AttributeCompression.octUnpack(compressed, normal, tangent, bitangent);

      expect(normal).toEqualEpsilon(
        Cartesian3.fromArray(originalNormals, (i / 2) * 3),
        CesiumMath.EPSILON2
      );
      expect(tangent).toEqualEpsilon(
        Cartesian3.fromArray(originalTangents, (i / 2) * 3),
        CesiumMath.EPSILON2
      );
      expect(bitangent).toEqualEpsilon(
        Cartesian3.fromArray(originalBitangents, (i / 2) * 3),
        CesiumMath.EPSILON2
      );
    }
  });

  it("splitLongitude does nothing for geometry not split by the IDL", function () {
    var instance = new GeometryInstance({
      geometry: PolygonGeometry.createGeometry(
        PolygonGeometry.fromPositions({
          positions: Cartesian3.fromDegreesArray([
            -1.0,
            1.0,
            -1.0,
            -1.0,
            1.0,
            -1.0,
            1.0,
            1.0,
          ]),
          vertexFormat: VertexFormat.POSITION_ONLY,
          granularity: 2.0 * CesiumMath.RADIANS_PER_DEGREE,
        })
      ),
    });

    var splitInstance = GeometryPipeline.splitLongitude(instance);
    expect(splitInstance).toBe(instance);
  });

  it("splitLongitude interpolates custom attributes for geometry split by the IDL", function () {
    var p0 = Cartesian3.fromDegrees(-179.0, 0.0);
    var p1 = Cartesian3.fromDegrees(179.0, 0.0);
    var p2 = Cartesian3.fromDegrees(-179.0, 1.0);

    var positions = new Float64Array([
      p0.x,
      p0.y,
      p0.z,
      p1.x,
      p1.y,
      p1.z,
      p2.x,
      p2.y,
      p2.z,
    ]);

    var vec4s = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0]);

    var vec3s = new Uint8Array([0, 0, 0, 0, 0, 255, 0, 0, 0]);

    var vec2s = new Uint8Array([0, 0, 0, 255, 0, 0]);

    var scalars = new Uint8Array([0, 255, 0]);

    var instance = new GeometryInstance({
      geometry: new Geometry({
        attributes: {
          position: new GeometryAttribute({
            componentDatatype: ComponentDatatype.DOUBLE,
            componentsPerAttribute: 3,
            values: positions,
          }),
          vec4s: new GeometryAttribute({
            componentDatatype: ComponentDatatype.UNSIGNED_BYTE,
            componentsPerAttribute: 4,
            values: vec4s,
          }),
          vec3s: new GeometryAttribute({
            componentDatatype: ComponentDatatype.UNSIGNED_BYTE,
            componentsPerAttribute: 3,
            values: vec3s,
          }),
          vec2s: new GeometryAttribute({
            componentDatatype: ComponentDatatype.UNSIGNED_BYTE,
            componentsPerAttribute: 2,
            values: vec2s,
          }),
          scalars: new GeometryAttribute({
            componentDatatype: ComponentDatatype.UNSIGNED_BYTE,
            componentsPerAttribute: 1,
            values: scalars,
          }),
        },
        indices: new Uint16Array([0, 1, 2]),
        primitiveType: PrimitiveType.TRIANGLES,
        boundingSphere: BoundingSphere.fromVertices(positions),
      }),
    });

    var splitInstance = GeometryPipeline.splitLongitude(instance);
    var eastHemisphereGeometry = splitInstance.eastHemisphereGeometry;
    expect(eastHemisphereGeometry.indices.length).toEqual(3);

    var newVec4s = eastHemisphereGeometry.attributes.vec4s.values;
    var newVec3s = eastHemisphereGeometry.attributes.vec3s.values;
    var newVec2s = eastHemisphereGeometry.attributes.vec2s.values;
    var newScalars = eastHemisphereGeometry.attributes.scalars.values;
    var i;
    var index;

    // Expect eastern hemisphere vertices to all be 255 or 127 at the end of the value
    expect(Array.prototype.indexOf.call(newScalars, 127)).not.toBe(-1);
    expect(Array.prototype.indexOf.call(newVec4s, 127)).not.toBe(-1);
    expect(Array.prototype.indexOf.call(newVec3s, 127)).not.toBe(-1);
    expect(Array.prototype.indexOf.call(newVec2s, 127)).not.toBe(-1);
    for (i = 0; i < 3; i++) {
      expect(newScalars[i] === 255 || newScalars[i] === 127).toBe(true);

      index = i * 2;
      expect(newVec2s[index]).toBe(0);
      expect(newVec2s[index + 1] === 255 || newVec2s[index + 1] === 127).toBe(
        true
      );

      index = i * 3;
      expect(newVec3s[index]).toBe(0);
      expect(newVec3s[index + 1]).toBe(0);
      expect(newVec3s[index + 2] === 255 || newVec3s[index + 2] === 127).toBe(
        true
      );

      index = i * 4;
      expect(newVec4s[index]).toBe(0);
      expect(newVec4s[index + 1]).toBe(0);
      expect(newVec4s[index + 2]).toBe(0);
      expect(newVec4s[index + 3] === 255 || newVec4s[index + 3] === 127).toBe(
        true
      );
    }

    var westHemisphereGeometry = splitInstance.westHemisphereGeometry;
    expect(westHemisphereGeometry.indices.length).toEqual(6);

    newVec4s = westHemisphereGeometry.attributes.vec4s.values;
    newVec3s = westHemisphereGeometry.attributes.vec3s.values;
    newVec2s = westHemisphereGeometry.attributes.vec2s.values;
    newScalars = westHemisphereGeometry.attributes.scalars.values;

    // Expect eastern hemisphere vertices to all be 0 or 127 at the end of the value
    expect(Array.prototype.indexOf.call(newScalars, 127)).not.toBe(-1);
    expect(Array.prototype.indexOf.call(newVec4s, 127)).not.toBe(-1);
    expect(Array.prototype.indexOf.call(newVec3s, 127)).not.toBe(-1);
    expect(Array.prototype.indexOf.call(newVec2s, 127)).not.toBe(-1);
    for (i = 0; i < 4; i++) {
      expect(newScalars[i] === 0 || newScalars[i] === 127).toBe(true);

      index = i * 2;
      expect(newVec2s[index]).toBe(0);
      expect(newVec2s[index + 1] === 0 || newVec2s[index + 1] === 127).toBe(
        true
      );

      index = i * 3;
      expect(newVec3s[index]).toBe(0);
      expect(newVec3s[index + 1]).toBe(0);
      expect(newVec3s[index + 2] === 0 || newVec3s[index + 2] === 127).toBe(
        true
      );

      index = i * 4;
      expect(newVec4s[index]).toBe(0);
      expect(newVec4s[index + 1]).toBe(0);
      expect(newVec4s[index + 2]).toBe(0);
      expect(newVec4s[index + 3] === 0 || newVec4s[index + 3] === 127).toBe(
        true
      );
    }
  });

  it("splitLongitude provides indices for an un-indexed triangle list", function () {
    var instance = new GeometryInstance({
      geometry: new Geometry({
        attributes: {
          position: new GeometryAttribute({
            componentDatatype: ComponentDatatype.DOUBLE,
            componentsPerAttribute: 3,
            values: new Float64Array([
              0.0,
              1.0,
              2.0,
              3.0,
              4.0,
              5.0,
              6.0,
              7.0,
              8.0,
              8.0,
              7.0,
              6.0,
              5.0,
              4.0,
              3.0,
              2.0,
              1.0,
              0.0,
            ]),
          }),
        },
        primitiveType: PrimitiveType.TRIANGLES,
      }),
    });

    GeometryPipeline.splitLongitude(instance);
    expect(instance.geometry.indices).toEqual([0, 1, 2, 3, 4, 5]);
  });

  it("splitLongitude returns unchanged geometry if indices are already defined for an un-indexed triangle list", function () {
    var instance = new GeometryInstance({
      geometry: new Geometry({
        attributes: {
          position: new GeometryAttribute({
            componentDatatype: ComponentDatatype.DOUBLE,
            componentsPerAttribute: 3,
            values: new Float64Array([
              0.0,
              1.0,
              2.0,
              3.0,
              4.0,
              5.0,
              6.0,
              7.0,
              8.0,
              8.0,
              7.0,
              6.0,
              5.0,
              4.0,
              3.0,
              2.0,
              1.0,
              0.0,
            ]),
          }),
        },
        primitiveType: PrimitiveType.TRIANGLES,
        indices: new Uint16Array([0, 1, 2, 3, 4, 5]),
      }),
    });

    GeometryPipeline.splitLongitude(instance);
    expect(instance.geometry.indices).toEqual([0, 1, 2, 3, 4, 5]);
  });

  it("splitLongitude throws when primitive type is TRIANGLES and number of vertices is less than 3", function () {
    var instance = new GeometryInstance({
      geometry: new Geometry({
        attributes: {
          position: new GeometryAttribute({
            componentDatatype: ComponentDatatype.DOUBLE,
            componentsPerAttribute: 3,
            values: new Float64Array([0.0, 1.0, 2.0]),
          }),
        },
        primitiveType: PrimitiveType.TRIANGLES,
      }),
    });

    expect(function () {
      GeometryPipeline.splitLongitude(instance);
    }).toThrowDeveloperError();
  });

  it("splitLongitude throws when primitive type is TRIANGLES and number of vertices is not a multiple of 3", function () {
    var instance = new GeometryInstance({
      geometry: new Geometry({
        attributes: {
          position: new GeometryAttribute({
            componentDatatype: ComponentDatatype.DOUBLE,
            componentsPerAttribute: 3,
            values: new Float64Array([
              0.0,
              1.0,
              2.0,
              3.0,
              4.0,
              5.0,
              6.0,
              7.0,
              8.0,
              8.0,
              7.0,
              6.0,
              5.0,
              4.0,
              3.0,
              2.0,
              1.0,
            ]),
          }),
        },
        primitiveType: PrimitiveType.TRIANGLES,
      }),
    });

    expect(function () {
      GeometryPipeline.splitLongitude(instance);
    }).toThrowDeveloperError();
  });

  it("splitLongitude creates indexed triangles for a triangle fan", function () {
    var instance = new GeometryInstance({
      geometry: new Geometry({
        attributes: {
          position: new GeometryAttribute({
            componentDatatype: ComponentDatatype.DOUBLE,
            componentsPerAttribute: 3,
            values: new Float64Array([
              0.0,
              1.0,
              2.0,
              3.0,
              4.0,
              5.0,
              6.0,
              7.0,
              8.0,
              8.0,
              7.0,
              6.0,
            ]),
          }),
        },
        primitiveType: PrimitiveType.TRIANGLE_FAN,
      }),
    });

    GeometryPipeline.splitLongitude(instance);
    expect(instance.geometry.primitiveType).toEqual(PrimitiveType.TRIANGLES);
    expect(instance.geometry.indices).toEqual([0, 1, 2, 2, 1, 3]);
  });

  it("splitLongitude throws when primitive type is TRIANGLE_FAN and number of vertices is less than 3", function () {
    var instance = new GeometryInstance({
      geometry: new Geometry({
        attributes: {
          position: new GeometryAttribute({
            componentDatatype: ComponentDatatype.DOUBLE,
            componentsPerAttribute: 3,
            values: new Float64Array([0.0, 1.0, 2.0]),
          }),
        },
        primitiveType: PrimitiveType.TRIANGLE_FAN,
      }),
    });

    expect(function () {
      GeometryPipeline.splitLongitude(instance);
    }).toThrowDeveloperError();
  });

  it("splitLongitude creates indexd triangles for triangle strips", function () {
    var instance = new GeometryInstance({
      geometry: new Geometry({
        attributes: {
          position: new GeometryAttribute({
            componentDatatype: ComponentDatatype.DOUBLE,
            componentsPerAttribute: 3,
            values: new Float64Array([
              0.0,
              1.0,
              2.0,
              3.0,
              4.0,
              5.0,
              6.0,
              7.0,
              8.0,
              8.0,
              7.0,
              6.0,
              5.0,
              4.0,
              3.0,
              2.0,
              1.0,
              0.0,
            ]),
          }),
        },
        primitiveType: PrimitiveType.TRIANGLE_STRIP,
      }),
    });

    GeometryPipeline.splitLongitude(instance);
    expect(instance.geometry.primitiveType).toEqual(PrimitiveType.TRIANGLES);
    expect(instance.geometry.indices).toEqual([
      0,
      1,
      2,
      0,
      2,
      3,
      3,
      2,
      4,
      3,
      4,
      5,
    ]);
  });

  it("splitLongitude throws when the primitive type is TRIANGLE_STRIP and number of vertices is less than 3", function () {
    var instance = new GeometryInstance({
      geometry: new Geometry({
        attributes: {
          position: new GeometryAttribute({
            componentDatatype: ComponentDatatype.DOUBLE,
            componentsPerAttribute: 3,
            values: new Float64Array([0.0, 1.0, 2.0]),
          }),
        },
        primitiveType: PrimitiveType.TRIANGLE_STRIP,
      }),
    });

    expect(function () {
      GeometryPipeline.splitLongitude(instance);
    }).toThrowDeveloperError();
  });

  it("splitLongitude creates indexed lines", function () {
    var instance = new GeometryInstance({
      geometry: new Geometry({
        attributes: {
          position: new GeometryAttribute({
            componentDatatype: ComponentDatatype.DOUBLE,
            componentsPerAttribute: 3,
            values: new Float64Array([
              0.0,
              1.0,
              2.0,
              3.0,
              4.0,
              5.0,
              6.0,
              7.0,
              8.0,
              8.0,
              7.0,
              6.0,
            ]),
          }),
        },
        primitiveType: PrimitiveType.LINES,
      }),
    });

    GeometryPipeline.splitLongitude(instance);
    expect(instance.geometry.indices).toEqual([0, 1, 2, 3]);
  });

  it("splitLongitude returns lines unchanged if indices are provided", function () {
    var instance = new GeometryInstance({
      geometry: new Geometry({
        attributes: {
          position: new GeometryAttribute({
            componentDatatype: ComponentDatatype.DOUBLE,
            componentsPerAttribute: 3,
            values: new Float64Array([
              0.0,
              1.0,
              2.0,
              3.0,
              4.0,
              5.0,
              6.0,
              7.0,
              8.0,
              8.0,
              7.0,
              6.0,
            ]),
          }),
        },
        primitiveType: PrimitiveType.LINES,
        indices: new Uint16Array([0, 1, 2, 3]),
      }),
    });

    GeometryPipeline.splitLongitude(instance);
    expect(instance.geometry.indices).toEqual([0, 1, 2, 3]);
  });

  it("splitLongitude throws when primitive type is LINES and number of vertices is less than 2", function () {
    var instance = new GeometryInstance({
      geometry: new Geometry({
        attributes: {
          position: new GeometryAttribute({
            componentDatatype: ComponentDatatype.DOUBLE,
            componentsPerAttribute: 3,
            values: new Float64Array([0.0, 1.0, 2.0]),
          }),
        },
        primitiveType: PrimitiveType.LINES,
      }),
    });

    expect(function () {
      GeometryPipeline.splitLongitude(instance);
    }).toThrowDeveloperError();
  });

  it("splitLongitude throws when primitive type is LINES and number of vertices is not a multiple 2", function () {
    var instance = new GeometryInstance({
      geometry: new Geometry({
        attributes: {
          position: new GeometryAttribute({
            componentDatatype: ComponentDatatype.DOUBLE,
            componentsPerAttribute: 3,
            values: new Float64Array([
              0.0,
              1.0,
              2.0,
              3.0,
              4.0,
              5.0,
              6.0,
              7.0,
              8.0,
            ]),
          }),
        },
        primitiveType: PrimitiveType.LINES,
      }),
    });

    expect(function () {
      GeometryPipeline.splitLongitude(instance);
    }).toThrowDeveloperError();
  });

  it("splitLongitude creates indexed lines from line strip", function () {
    var instance = new GeometryInstance({
      geometry: new Geometry({
        attributes: {
          position: new GeometryAttribute({
            componentDatatype: ComponentDatatype.DOUBLE,
            componentsPerAttribute: 3,
            values: new Float64Array([
              0.0,
              1.0,
              2.0,
              3.0,
              4.0,
              5.0,
              6.0,
              7.0,
              8.0,
              8.0,
              7.0,
              6.0,
            ]),
          }),
        },
        primitiveType: PrimitiveType.LINE_STRIP,
      }),
    });

    GeometryPipeline.splitLongitude(instance);
    expect(instance.geometry.primitiveType).toEqual(PrimitiveType.LINES);
    expect(instance.geometry.indices).toEqual([0, 1, 1, 2, 2, 3]);
  });

  it("splitLongitude throws when primitive type is LINE_STRIP and number of vertices is less than 2", function () {
    var instance = new GeometryInstance({
      geometry: new Geometry({
        attributes: {
          position: new GeometryAttribute({
            componentDatatype: ComponentDatatype.DOUBLE,
            componentsPerAttribute: 3,
            values: new Float64Array([0.0, 1.0, 2.0]),
          }),
        },
        primitiveType: PrimitiveType.LINE_STRIP,
      }),
    });

    expect(function () {
      GeometryPipeline.splitLongitude(instance);
    }).toThrowDeveloperError();
  });

  it("splitLongitude creates indexed lines from line loops", function () {
    var instance = new GeometryInstance({
      geometry: new Geometry({
        attributes: {
          position: new GeometryAttribute({
            componentDatatype: ComponentDatatype.DOUBLE,
            componentsPerAttribute: 3,
            values: new Float64Array([
              0.0,
              1.0,
              2.0,
              3.0,
              4.0,
              5.0,
              6.0,
              7.0,
              8.0,
              8.0,
              7.0,
              6.0,
            ]),
          }),
        },
        primitiveType: PrimitiveType.LINE_LOOP,
      }),
    });

    GeometryPipeline.splitLongitude(instance);
    expect(instance.geometry.primitiveType).toEqual(PrimitiveType.LINES);
    expect(instance.geometry.indices).toEqual([0, 1, 1, 2, 2, 3, 3, 0]);
  });

  it("splitLongitude throws when the primitive type is LINE_LOOP and number of vertices is less than 2", function () {
    var instance = new GeometryInstance({
      geometry: new Geometry({
        attributes: {
          position: new GeometryAttribute({
            componentDatatype: ComponentDatatype.DOUBLE,
            componentsPerAttribute: 3,
            values: new Float64Array([0.0, 1.0, 2.0]),
          }),
        },
        primitiveType: PrimitiveType.LINE_LOOP,
      }),
    });

    expect(function () {
      GeometryPipeline.splitLongitude(instance);
    }).toThrowDeveloperError();
  });

  it("splitLongitude subdivides triangle crossing the international date line, p0 behind", function () {
    var instance = new GeometryInstance({
      geometry: new Geometry({
        attributes: {
          position: new GeometryAttribute({
            componentDatatype: ComponentDatatype.DOUBLE,
            componentsPerAttribute: 3,
            values: new Float64Array([
              -1.0,
              -1.0,
              0.0,
              -1.0,
              1.0,
              2.0,
              -1.0,
              2.0,
              2.0,
            ]),
          }),
        },
        indices: new Uint16Array([0, 1, 2]),
        primitiveType: PrimitiveType.TRIANGLES,
      }),
    });
    GeometryPipeline.splitLongitude(instance);
    expect(instance.geometry).not.toBeDefined();

    expect(instance.westHemisphereGeometry).toBeDefined();
    expect(instance.westHemisphereGeometry.indices).toBeDefined();
    expect(instance.westHemisphereGeometry.indices.length).toEqual(3);
    expect(instance.westHemisphereGeometry.attributes.position).toBeDefined();
    expect(
      instance.westHemisphereGeometry.attributes.position.values.length
    ).toEqual(3 * 3);

    expect(instance.eastHemisphereGeometry).toBeDefined();
    expect(instance.eastHemisphereGeometry.indices).toBeDefined();
    expect(instance.eastHemisphereGeometry.indices.length).toEqual(6);
    expect(instance.eastHemisphereGeometry.attributes.position).toBeDefined();
    expect(
      instance.eastHemisphereGeometry.attributes.position.values.length
    ).toEqual(5 * 3);
  });

  it("splitLongitude subdivides triangle crossing the international date line, p1 behind", function () {
    var instance = new GeometryInstance({
      geometry: new Geometry({
        attributes: {
          position: new GeometryAttribute({
            componentDatatype: ComponentDatatype.DOUBLE,
            componentsPerAttribute: 3,
            values: new Float64Array([
              -1.0,
              1.0,
              2.0,
              -1.0,
              -1.0,
              0.0,
              -1.0,
              2.0,
              2.0,
            ]),
          }),
        },
        indices: new Uint16Array([0, 1, 2]),
        primitiveType: PrimitiveType.TRIANGLES,
      }),
    });
    GeometryPipeline.splitLongitude(instance);
    expect(instance.geometry).not.toBeDefined();

    expect(instance.westHemisphereGeometry).toBeDefined();
    expect(instance.westHemisphereGeometry.indices).toBeDefined();
    expect(instance.westHemisphereGeometry.indices.length).toEqual(3);
    expect(instance.westHemisphereGeometry.attributes.position).toBeDefined();
    expect(
      instance.westHemisphereGeometry.attributes.position.values.length
    ).toEqual(3 * 3);

    expect(instance.eastHemisphereGeometry).toBeDefined();
    expect(instance.eastHemisphereGeometry.indices).toBeDefined();
    expect(instance.eastHemisphereGeometry.indices.length).toEqual(6);
    expect(instance.eastHemisphereGeometry.attributes.position).toBeDefined();
    expect(
      instance.eastHemisphereGeometry.attributes.position.values.length
    ).toEqual(5 * 3);
  });

  it("splitLongitude subdivides triangle crossing the international date line, p2 behind", function () {
    var instance = new GeometryInstance({
      geometry: new Geometry({
        attributes: {
          position: new GeometryAttribute({
            componentDatatype: ComponentDatatype.DOUBLE,
            componentsPerAttribute: 3,
            values: new Float64Array([
              -1.0,
              1.0,
              2.0,
              -1.0,
              2.0,
              2.0,
              -1.0,
              -1.0,
              0.0,
            ]),
          }),
        },
        indices: new Uint16Array([0, 1, 2]),
        primitiveType: PrimitiveType.TRIANGLES,
      }),
    });
    GeometryPipeline.splitLongitude(instance);
    expect(instance.geometry).not.toBeDefined();

    expect(instance.westHemisphereGeometry).toBeDefined();
    expect(instance.westHemisphereGeometry.indices).toBeDefined();
    expect(instance.westHemisphereGeometry.indices.length).toEqual(3);
    expect(instance.westHemisphereGeometry.attributes.position).toBeDefined();
    expect(
      instance.westHemisphereGeometry.attributes.position.values.length
    ).toEqual(3 * 3);

    expect(instance.eastHemisphereGeometry).toBeDefined();
    expect(instance.eastHemisphereGeometry.indices).toBeDefined();
    expect(instance.eastHemisphereGeometry.indices.length).toEqual(6);
    expect(instance.eastHemisphereGeometry.attributes.position).toBeDefined();
    expect(
      instance.eastHemisphereGeometry.attributes.position.values.length
    ).toEqual(5 * 3);
  });

  it("splitLongitude subdivides triangle crossing the international date line, p0 ahead", function () {
    var instance = new GeometryInstance({
      geometry: new Geometry({
        attributes: {
          position: new GeometryAttribute({
            componentDatatype: ComponentDatatype.DOUBLE,
            componentsPerAttribute: 3,
            values: new Float64Array([
              -1.0,
              1.0,
              0.0,
              -1.0,
              -1.0,
              0.0,
              -2.0,
              -1.0,
              0.0,
            ]),
          }),
        },
        indices: new Uint16Array([0, 1, 2]),
        primitiveType: PrimitiveType.TRIANGLES,
      }),
    });
    GeometryPipeline.splitLongitude(instance);
    expect(instance.geometry).not.toBeDefined();

    expect(instance.westHemisphereGeometry).toBeDefined();
    expect(instance.westHemisphereGeometry.indices).toBeDefined();
    expect(instance.westHemisphereGeometry.indices.length).toEqual(6);
    expect(instance.westHemisphereGeometry.attributes.position).toBeDefined();
    expect(
      instance.westHemisphereGeometry.attributes.position.values.length
    ).toEqual(5 * 3);

    expect(instance.eastHemisphereGeometry).toBeDefined();
    expect(instance.eastHemisphereGeometry.indices).toBeDefined();
    expect(instance.eastHemisphereGeometry.indices.length).toEqual(3);
    expect(instance.eastHemisphereGeometry.attributes.position).toBeDefined();
    expect(
      instance.eastHemisphereGeometry.attributes.position.values.length
    ).toEqual(3 * 3);
  });

  it("splitLongitude subdivides triangle crossing the international date line, p1 ahead", function () {
    var instance = new GeometryInstance({
      geometry: new Geometry({
        attributes: {
          position: new GeometryAttribute({
            componentDatatype: ComponentDatatype.DOUBLE,
            componentsPerAttribute: 3,
            values: new Float64Array([
              -2.0,
              -1.0,
              0.0,
              -1.0,
              1.0,
              0.0,
              -1.0,
              -1.0,
              0.0,
            ]),
          }),
        },
        indices: new Uint16Array([0, 1, 2]),
        primitiveType: PrimitiveType.TRIANGLES,
      }),
    });
    GeometryPipeline.splitLongitude(instance);
    expect(instance.geometry).not.toBeDefined();

    expect(instance.westHemisphereGeometry).toBeDefined();
    expect(instance.westHemisphereGeometry.indices).toBeDefined();
    expect(instance.westHemisphereGeometry.indices.length).toEqual(6);
    expect(instance.westHemisphereGeometry.attributes.position).toBeDefined();
    expect(
      instance.westHemisphereGeometry.attributes.position.values.length
    ).toEqual(5 * 3);

    expect(instance.eastHemisphereGeometry).toBeDefined();
    expect(instance.eastHemisphereGeometry.indices).toBeDefined();
    expect(instance.eastHemisphereGeometry.indices.length).toEqual(3);
    expect(instance.eastHemisphereGeometry.attributes.position).toBeDefined();
    expect(
      instance.eastHemisphereGeometry.attributes.position.values.length
    ).toEqual(3 * 3);
  });

  it("splitLongitude subdivides triangle crossing the international date line, p2 ahead", function () {
    var instance = new GeometryInstance({
      geometry: new Geometry({
        attributes: {
          position: new GeometryAttribute({
            componentDatatype: ComponentDatatype.DOUBLE,
            componentsPerAttribute: 3,
            values: new Float64Array([
              -1.0,
              -1.0,
              0.0,
              -2.0,
              -1.0,
              0.0,
              -1.0,
              1.0,
              0.0,
            ]),
          }),
        },
        indices: new Uint16Array([0, 1, 2]),
        primitiveType: PrimitiveType.TRIANGLES,
      }),
    });
    GeometryPipeline.splitLongitude(instance);
    expect(instance.geometry).not.toBeDefined();

    expect(instance.westHemisphereGeometry).toBeDefined();
    expect(instance.westHemisphereGeometry.indices).toBeDefined();
    expect(instance.westHemisphereGeometry.indices.length).toEqual(6);
    expect(instance.westHemisphereGeometry.attributes.position).toBeDefined();
    expect(
      instance.westHemisphereGeometry.attributes.position.values.length
    ).toEqual(5 * 3);

    expect(instance.eastHemisphereGeometry).toBeDefined();
    expect(instance.eastHemisphereGeometry.indices).toBeDefined();
    expect(instance.eastHemisphereGeometry.indices.length).toEqual(3);
    expect(instance.eastHemisphereGeometry.attributes.position).toBeDefined();
    expect(
      instance.eastHemisphereGeometry.attributes.position.values.length
    ).toEqual(3 * 3);
  });

  it("splitLongitude returns offset triangle that touches the international date line", function () {
    var instance = new GeometryInstance({
      geometry: new Geometry({
        attributes: {
          position: new GeometryAttribute({
            componentDatatype: ComponentDatatype.DOUBLE,
            componentsPerAttribute: 3,
            values: new Float64Array([
              -1.0,
              0.0,
              1.0,
              -1.0,
              CesiumMath.EPSILON14,
              2.0,
              -2.0,
              2.0,
              2.0,
            ]),
          }),
        },
        indices: new Uint16Array([0, 1, 2]),
        primitiveType: PrimitiveType.TRIANGLES,
      }),
    });
    GeometryPipeline.splitLongitude(instance);
    var geometry = instance.geometry;

    expect(geometry.indices).toEqual([0, 1, 2]);

    var positions = geometry.attributes.position.values;
    expect(positions).toEqual([
      -1.0,
      CesiumMath.EPSILON6,
      1.0,
      -1.0,
      CesiumMath.EPSILON6,
      2.0,
      -2.0,
      2.0,
      2.0,
    ]);
    expect(positions.length).toEqual(3 * 3);
  });

  it("splitLongitude returns the same points if the triangle doesn't cross the international date line, behind", function () {
    var instance = new GeometryInstance({
      geometry: new Geometry({
        attributes: {
          position: new GeometryAttribute({
            componentDatatype: ComponentDatatype.DOUBLE,
            componentsPerAttribute: 3,
            values: new Float64Array([
              -1.0,
              -1.0,
              1.0,
              -1.0,
              -2.0,
              1.0,
              -1.0,
              -2.0,
              2.0,
            ]),
          }),
        },
        indices: new Uint16Array([0, 1, 2]),
        primitiveType: PrimitiveType.TRIANGLES,
      }),
    });
    GeometryPipeline.splitLongitude(instance);
    var geometry = instance.geometry;

    expect(geometry.indices).toEqual([0, 1, 2]);

    var positions = geometry.attributes.position.values;
    expect(positions).toEqual([
      -1.0,
      -1.0,
      1.0,
      -1.0,
      -2.0,
      1.0,
      -1.0,
      -2.0,
      2.0,
    ]);
    expect(positions.length).toEqual(3 * 3);
  });

  it("splitLongitude returns the same points if the triangle doesn't cross the international date line, ahead", function () {
    var instance = new GeometryInstance({
      geometry: new Geometry({
        attributes: {
          position: new GeometryAttribute({
            componentDatatype: ComponentDatatype.DOUBLE,
            componentsPerAttribute: 3,
            values: new Float64Array([
              -1.0,
              1.0,
              1.0,
              -1.0,
              2.0,
              1.0,
              -1.0,
              2.0,
              2.0,
            ]),
          }),
        },
        indices: new Uint16Array([0, 1, 2]),
        primitiveType: PrimitiveType.TRIANGLES,
      }),
    });
    GeometryPipeline.splitLongitude(instance);
    var geometry = instance.geometry;

    expect(geometry.indices).toEqual([0, 1, 2]);

    var positions = geometry.attributes.position.values;
    expect(positions).toEqual([-1.0, 1.0, 1.0, -1.0, 2.0, 1.0, -1.0, 2.0, 2.0]);
    expect(positions.length).toEqual(3 * 3);
  });

  it("splitLongitude returns the same points if the triangle doesn't cross the international date line, positive x", function () {
    var instance = new GeometryInstance({
      geometry: new Geometry({
        attributes: {
          position: new GeometryAttribute({
            componentDatatype: ComponentDatatype.DOUBLE,
            componentsPerAttribute: 3,
            values: new Float64Array([
              1.0,
              1.0,
              1.0,
              1.0,
              2.0,
              1.0,
              1.0,
              2.0,
              2.0,
            ]),
          }),
        },
        indices: new Uint16Array([0, 1, 2]),
        primitiveType: PrimitiveType.TRIANGLES,
      }),
    });
    GeometryPipeline.splitLongitude(instance);
    var geometry = instance.geometry;

    expect(geometry.indices).toEqual([0, 1, 2]);

    var positions = geometry.attributes.position.values;
    expect(positions).toEqual([1.0, 1.0, 1.0, 1.0, 2.0, 1.0, 1.0, 2.0, 2.0]);
    expect(positions.length).toEqual(3 * 3);
  });

  it("splitLongitude computes all attributes for a triangle crossing the international date line", function () {
    var instance = new GeometryInstance({
      geometry: new Geometry({
        attributes: {
          position: new GeometryAttribute({
            componentDatatype: ComponentDatatype.DOUBLE,
            componentsPerAttribute: 3,
            values: new Float64Array([
              -2.0,
              -1.0,
              0.0,
              -3.0,
              1.0,
              0.0,
              -1.0,
              1.0,
              0.0,
            ]),
          }),
          normal: new GeometryAttribute({
            componentDatatype: ComponentDatatype.FLOAT,
            componentsPerAttribute: 3,
            values: new Float32Array([
              0.0,
              0.0,
              1.0,
              0.0,
              0.0,
              1.0,
              0.0,
              0.0,
              1.0,
            ]),
          }),
          tangent: new GeometryAttribute({
            componentDatatype: ComponentDatatype.FLOAT,
            componentsPerAttribute: 3,
            values: new Float32Array([
              -1.0,
              0.0,
              0.0,
              -1.0,
              0.0,
              0.0,
              -1.0,
              0.0,
              0.0,
            ]),
          }),
          bitangent: new GeometryAttribute({
            componentDatatype: ComponentDatatype.FLOAT,
            componentsPerAttribute: 3,
            values: new Float32Array([
              0.0,
              -1.0,
              0.0,
              0.0,
              -1.0,
              0.0,
              0.0,
              -1.0,
              0.0,
            ]),
          }),
          st: new GeometryAttribute({
            componentDatatype: ComponentDatatype.FLOAT,
            componentsPerAttribute: 2,
            values: new Float32Array([0.0, 0.0, 1.0, 0.0, 0.5, 0.5]),
          }),
        },
        indices: new Uint16Array([1, 2, 0]),
        primitiveType: PrimitiveType.TRIANGLES,
      }),
    });
    GeometryPipeline.splitLongitude(instance);
    expect(instance.geometry).not.toBeDefined();

    expect(instance.westHemisphereGeometry).toBeDefined();
    expect(instance.westHemisphereGeometry.indices).toBeDefined();
    expect(instance.westHemisphereGeometry.indices.length).toEqual(3);
    expect(instance.westHemisphereGeometry.attributes.position).toBeDefined();
    expect(
      instance.westHemisphereGeometry.attributes.position.values.length
    ).toEqual(3 * 3);
    expect(instance.westHemisphereGeometry.attributes.normal).toBeDefined();
    expect(
      instance.westHemisphereGeometry.attributes.normal.values.length
    ).toEqual(3 * 3);
    expect(instance.westHemisphereGeometry.attributes.bitangent).toBeDefined();
    expect(
      instance.westHemisphereGeometry.attributes.bitangent.values.length
    ).toEqual(3 * 3);
    expect(instance.westHemisphereGeometry.attributes.tangent).toBeDefined();
    expect(
      instance.westHemisphereGeometry.attributes.tangent.values.length
    ).toEqual(3 * 3);
    expect(instance.westHemisphereGeometry.attributes.st).toBeDefined();
    expect(instance.westHemisphereGeometry.attributes.st.values.length).toEqual(
      3 * 2
    );

    expect(instance.eastHemisphereGeometry).toBeDefined();
    expect(instance.eastHemisphereGeometry.indices).toBeDefined();
    expect(instance.eastHemisphereGeometry.indices.length).toEqual(6);
    expect(instance.eastHemisphereGeometry.attributes.position).toBeDefined();
    expect(
      instance.eastHemisphereGeometry.attributes.position.values.length
    ).toEqual(5 * 3);
    expect(instance.eastHemisphereGeometry.attributes.normal).toBeDefined();
    expect(
      instance.eastHemisphereGeometry.attributes.normal.values.length
    ).toEqual(5 * 3);
    expect(instance.eastHemisphereGeometry.attributes.bitangent).toBeDefined();
    expect(
      instance.eastHemisphereGeometry.attributes.bitangent.values.length
    ).toEqual(5 * 3);
    expect(instance.eastHemisphereGeometry.attributes.tangent).toBeDefined();
    expect(
      instance.eastHemisphereGeometry.attributes.tangent.values.length
    ).toEqual(5 * 3);
    expect(instance.eastHemisphereGeometry.attributes.st).toBeDefined();
    expect(instance.eastHemisphereGeometry.attributes.st.values.length).toEqual(
      5 * 2
    );
  });

  it("splitLongitude subdivides line crossing the international date line", function () {
    var instance = new GeometryInstance({
      geometry: new Geometry({
        attributes: {
          position: new GeometryAttribute({
            componentDatatype: ComponentDatatype.DOUBLE,
            componentsPerAttribute: 3,
            values: new Float64Array([-1.0, -1.0, 0.0, -1.0, 1.0, 2.0]),
          }),
        },
        indices: new Uint16Array([0, 1]),
        primitiveType: PrimitiveType.LINES,
      }),
    });
    GeometryPipeline.splitLongitude(instance);
    expect(instance.geometry).not.toBeDefined();

    expect(instance.westHemisphereGeometry).toBeDefined();
    expect(instance.westHemisphereGeometry.indices).toBeDefined();
    expect(instance.westHemisphereGeometry.indices.length).toEqual(2);
    expect(instance.westHemisphereGeometry.attributes.position).toBeDefined();
    expect(
      instance.westHemisphereGeometry.attributes.position.values.length
    ).toEqual(2 * 3);

    expect(instance.eastHemisphereGeometry).toBeDefined();
    expect(instance.eastHemisphereGeometry.indices).toBeDefined();
    expect(instance.eastHemisphereGeometry.indices.length).toEqual(2);
    expect(instance.eastHemisphereGeometry.attributes.position).toBeDefined();
    expect(
      instance.eastHemisphereGeometry.attributes.position.values.length
    ).toEqual(2 * 3);
  });

  it("splitLongitude returns offset line that touches the international date line", function () {
    var instance = new GeometryInstance({
      geometry: new Geometry({
        attributes: {
          position: new GeometryAttribute({
            componentDatatype: ComponentDatatype.DOUBLE,
            componentsPerAttribute: 3,
            values: new Float64Array([-1.0, 0.0, 0.0, -1.0, 1.0, 2.0]),
          }),
        },
        indices: new Uint16Array([0, 1]),
        primitiveType: PrimitiveType.LINES,
      }),
    });
    GeometryPipeline.splitLongitude(instance);
    var geometry = instance.geometry;

    expect(geometry.indices).toEqual([0, 1]);

    var positions = geometry.attributes.position.values;
    expect(positions).toEqual([-1.0, CesiumMath.EPSILON6, 0.0, -1.0, 1.0, 2.0]);
  });

  it("splitLongitude returns the same points if the line doesn't cross the international date line", function () {
    var instance = new GeometryInstance({
      geometry: new Geometry({
        attributes: {
          position: new GeometryAttribute({
            componentDatatype: ComponentDatatype.DOUBLE,
            componentsPerAttribute: 3,
            values: new Float64Array([1.0, 1.0, 0.0, 1.0, 1.0, 2.0]),
          }),
        },
        indices: new Uint16Array([0, 1]),
        primitiveType: PrimitiveType.LINES,
      }),
    });
    GeometryPipeline.splitLongitude(instance);
    var geometry = instance.geometry;

    expect(geometry.indices).toEqual([0, 1]);

    var positions = geometry.attributes.position.values;
    expect(positions).toEqual([1.0, 1.0, 0.0, 1.0, 1.0, 2.0]);
  });

  it("splitLongitude does nothing for points", function () {
    var instance = new GeometryInstance({
      geometry: new Geometry({
        attributes: {
          position: new GeometryAttribute({
            componentDatatype: ComponentDatatype.DOUBLE,
            componentsPerAttribute: 3,
            values: new Float64Array([1.0, 1.0, 0.0, 1.0, 1.0, 2.0]),
          }),
        },
        primitiveType: PrimitiveType.POINTS,
      }),
    });
    GeometryPipeline.splitLongitude(instance);
    var geometry = instance.geometry;

    expect(geometry.indices).not.toBeDefined();

    var positions = geometry.attributes.position.values;
    expect(positions).toEqual([1.0, 1.0, 0.0, 1.0, 1.0, 2.0]);
  });

  describe("splitLongitude polylines", function () {
    it("subdivides wide line crossing the international date line", function () {
      var instance = new GeometryInstance({
        geometry: new Geometry({
          attributes: {
            position: new GeometryAttribute({
              componentDatatype: ComponentDatatype.DOUBLE,
              componentsPerAttribute: 3,
              values: new Float64Array([
                -1.0,
                -1.0,
                0.0,
                -1.0,
                -1.0,
                0.0,
                -1.0,
                1.0,
                2.0,
                -1.0,
                1.0,
                2.0,
              ]),
            }),
            nextPosition: new GeometryAttribute({
              componentDatatype: ComponentDatatype.DOUBLE,
              componentsPerAttribute: 3,
              values: new Float64Array([
                -1.0,
                1.0,
                2.0,
                -1.0,
                1.0,
                2.0,
                -1.0,
                2.0,
                3.0,
                -1.0,
                2.0,
                3.0,
              ]),
            }),
            prevPosition: new GeometryAttribute({
              componentDatatype: ComponentDatatype.DOUBLE,
              componentsPerAttribute: 3,
              values: new Float64Array([
                -1.0,
                -2.0,
                -1.0,
                -1.0,
                -2.0,
                -1.0,
                -1.0,
                -1.0,
                0.0,
                -1.0,
                -1.0,
                0.0,
              ]),
            }),
            expandAndWidth: new GeometryAttribute({
              componentDatatype: ComponentDatatype.FLOAT,
              componentsPerAttribute: 2,
              values: new Float32Array([
                -1.0,
                5.0,
                1.0,
                5.0,
                -1.0,
                -5.0,
                1.0,
                -5.0,
              ]),
            }),
          },
          indices: new Uint16Array([0, 2, 1, 1, 2, 3]),
          primitiveType: PrimitiveType.TRIANGLES,
          geometryType: GeometryType.POLYLINES,
        }),
      });
      GeometryPipeline.splitLongitude(instance);
      expect(instance.geometry).not.toBeDefined();

      expect(instance.westHemisphereGeometry).toBeDefined();
      expect(instance.westHemisphereGeometry.indices).toBeDefined();
      expect(instance.westHemisphereGeometry.indices.length).toEqual(6);
      expect(instance.westHemisphereGeometry.attributes.position).toBeDefined();
      expect(
        instance.westHemisphereGeometry.attributes.position.values.length
      ).toEqual(4 * 3);
      expect(
        instance.westHemisphereGeometry.attributes.nextPosition
      ).toBeDefined();
      expect(
        instance.westHemisphereGeometry.attributes.nextPosition.values.length
      ).toEqual(4 * 3);
      expect(
        instance.westHemisphereGeometry.attributes.prevPosition
      ).toBeDefined();
      expect(
        instance.westHemisphereGeometry.attributes.prevPosition.values.length
      ).toEqual(4 * 3);
      expect(
        instance.westHemisphereGeometry.attributes.expandAndWidth
      ).toBeDefined();
      expect(
        instance.westHemisphereGeometry.attributes.expandAndWidth.values.length
      ).toEqual(4 * 2);

      expect(instance.eastHemisphereGeometry).toBeDefined();
      expect(instance.eastHemisphereGeometry.indices).toBeDefined();
      expect(instance.eastHemisphereGeometry.indices.length).toEqual(6);
      expect(instance.eastHemisphereGeometry.attributes.position).toBeDefined();
      expect(
        instance.eastHemisphereGeometry.attributes.position.values.length
      ).toEqual(4 * 3);
      expect(
        instance.eastHemisphereGeometry.attributes.nextPosition
      ).toBeDefined();
      expect(
        instance.eastHemisphereGeometry.attributes.nextPosition.values.length
      ).toEqual(4 * 3);
      expect(
        instance.eastHemisphereGeometry.attributes.prevPosition
      ).toBeDefined();
      expect(
        instance.eastHemisphereGeometry.attributes.prevPosition.values.length
      ).toEqual(4 * 3);
      expect(
        instance.eastHemisphereGeometry.attributes.expandAndWidth
      ).toBeDefined();
      expect(
        instance.eastHemisphereGeometry.attributes.expandAndWidth.values.length
      ).toEqual(4 * 2);
    });

    it("returns offset wide line with first point on the IDL and the second is east", function () {
      var instance = new GeometryInstance({
        geometry: new Geometry({
          attributes: {
            position: new GeometryAttribute({
              componentDatatype: ComponentDatatype.DOUBLE,
              componentsPerAttribute: 3,
              values: new Float64Array([
                -1.0,
                0.0,
                0.0,
                -1.0,
                0.0,
                0.0,
                -1.0,
                1.0,
                2.0,
                -1.0,
                1.0,
                2.0,
              ]),
            }),
            nextPosition: new GeometryAttribute({
              componentDatatype: ComponentDatatype.DOUBLE,
              componentsPerAttribute: 3,
              values: new Float64Array([
                -1.0,
                1.0,
                2.0,
                -1.0,
                1.0,
                2.0,
                -1.0,
                2.0,
                3.0,
                -1.0,
                2.0,
                3.0,
              ]),
            }),
            prevPosition: new GeometryAttribute({
              componentDatatype: ComponentDatatype.DOUBLE,
              componentsPerAttribute: 3,
              values: new Float64Array([
                -1.0,
                -2.0,
                -1.0,
                -1.0,
                -2.0,
                -1.0,
                -1.0,
                0.0,
                0.0,
                -1.0,
                0.0,
                0.0,
              ]),
            }),
            expandAndWidth: new GeometryAttribute({
              componentDatatype: ComponentDatatype.FLOAT,
              componentsPerAttribute: 2,
              values: new Float32Array([
                -1.0,
                5.0,
                1.0,
                5.0,
                -1.0,
                -5.0,
                1.0,
                -5.0,
              ]),
            }),
          },
          indices: new Uint16Array([0, 2, 1, 1, 2, 3]),
          primitiveType: PrimitiveType.TRIANGLES,
          geometryType: GeometryType.POLYLINES,
        }),
      });
      GeometryPipeline.splitLongitude(instance);
      var geometry = instance.geometry;

      expect(geometry.indices).toEqual([0, 2, 1, 1, 2, 3]);

      var positions = geometry.attributes.position.values;
      var nextPositions = geometry.attributes.nextPosition.values;
      var prevPositions = geometry.attributes.prevPosition.values;

      expect(positions).toEqual([
        -1.0,
        CesiumMath.EPSILON6,
        0.0,
        -1.0,
        CesiumMath.EPSILON6,
        0.0,
        -1.0,
        1.0,
        2.0,
        -1.0,
        1.0,
        2.0,
      ]);
      expect(prevPositions).toEqual([
        -1.0,
        CesiumMath.EPSILON6,
        0.0,
        -1.0,
        CesiumMath.EPSILON6,
        0.0,
        -1.0,
        CesiumMath.EPSILON6,
        0.0,
        -1.0,
        CesiumMath.EPSILON6,
        0.0,
      ]);
      expect(nextPositions).toEqual([
        -1.0,
        1.0,
        2.0,
        -1.0,
        1.0,
        2.0,
        -1.0,
        2.0,
        3.0,
        -1.0,
        2.0,
        3.0,
      ]);
    });

    it("returns offset wide line with first point on the IDL and the second is west", function () {
      var instance = new GeometryInstance({
        geometry: new Geometry({
          attributes: {
            position: new GeometryAttribute({
              componentDatatype: ComponentDatatype.DOUBLE,
              componentsPerAttribute: 3,
              values: new Float64Array([
                -1.0,
                0.0,
                0.0,
                -1.0,
                0.0,
                0.0,
                -1.0,
                -1.0,
                2.0,
                -1.0,
                -1.0,
                2.0,
              ]),
            }),
            nextPosition: new GeometryAttribute({
              componentDatatype: ComponentDatatype.DOUBLE,
              componentsPerAttribute: 3,
              values: new Float64Array([
                -1.0,
                -1.0,
                2.0,
                -1.0,
                -1.0,
                2.0,
                -1.0,
                -2.0,
                0.0,
                -1.0,
                -2.0,
                0.0,
              ]),
            }),
            prevPosition: new GeometryAttribute({
              componentDatatype: ComponentDatatype.DOUBLE,
              componentsPerAttribute: 3,
              values: new Float64Array([
                -1.0,
                1.0,
                -1.0,
                -1.0,
                1.0,
                -1.0,
                -1.0,
                0.0,
                0.0,
                -1.0,
                0.0,
                0.0,
              ]),
            }),
            expandAndWidth: new GeometryAttribute({
              componentDatatype: ComponentDatatype.FLOAT,
              componentsPerAttribute: 2,
              values: new Float32Array([
                -1.0,
                5.0,
                1.0,
                5.0,
                -1.0,
                -5.0,
                1.0,
                -5.0,
              ]),
            }),
          },
          indices: new Uint16Array([0, 2, 1, 1, 2, 3]),
          primitiveType: PrimitiveType.TRIANGLES,
          geometryType: GeometryType.POLYLINES,
        }),
      });
      GeometryPipeline.splitLongitude(instance);
      var geometry = instance.geometry;

      expect(geometry.indices).toEqual([0, 2, 1, 1, 2, 3]);

      var positions = geometry.attributes.position.values;
      var nextPositions = geometry.attributes.nextPosition.values;
      var prevPositions = geometry.attributes.prevPosition.values;

      expect(positions).toEqual([
        -1.0,
        -CesiumMath.EPSILON6,
        0.0,
        -1.0,
        -CesiumMath.EPSILON6,
        0.0,
        -1.0,
        -1.0,
        2.0,
        -1.0,
        -1.0,
        2.0,
      ]);
      expect(prevPositions).toEqual([
        -1.0,
        -CesiumMath.EPSILON6,
        0.0,
        -1.0,
        -CesiumMath.EPSILON6,
        0.0,
        -1.0,
        -CesiumMath.EPSILON6,
        0.0,
        -1.0,
        -CesiumMath.EPSILON6,
        0.0,
      ]);
      expect(nextPositions).toEqual([
        -1.0,
        -1.0,
        2.0,
        -1.0,
        -1.0,
        2.0,
        -1.0,
        -2.0,
        0.0,
        -1.0,
        -2.0,
        0.0,
      ]);
    });

    it("returns offset wide line with first point is east and the second is on the IDL", function () {
      var instance = new GeometryInstance({
        geometry: new Geometry({
          attributes: {
            position: new GeometryAttribute({
              componentDatatype: ComponentDatatype.DOUBLE,
              componentsPerAttribute: 3,
              values: new Float64Array([
                -1.0,
                1.0,
                2.0,
                -1.0,
                1.0,
                2.0,
                -1.0,
                0.0,
                0.0,
                -1.0,
                0.0,
                0.0,
              ]),
            }),
            nextPosition: new GeometryAttribute({
              componentDatatype: ComponentDatatype.DOUBLE,
              componentsPerAttribute: 3,
              values: new Float64Array([
                -1.0,
                0.0,
                0.0,
                -1.0,
                0.0,
                0.0,
                -1.0,
                -1.0,
                2.0,
                -1.0,
                -1.0,
                2.0,
              ]),
            }),
            prevPosition: new GeometryAttribute({
              componentDatatype: ComponentDatatype.DOUBLE,
              componentsPerAttribute: 3,
              values: new Float64Array([
                -2.0,
                2.0,
                2.0,
                -1.0,
                2.0,
                2.0,
                -1.0,
                1.0,
                2.0,
                -1.0,
                1.0,
                2.0,
              ]),
            }),
            expandAndWidth: new GeometryAttribute({
              componentDatatype: ComponentDatatype.FLOAT,
              componentsPerAttribute: 2,
              values: new Float32Array([
                -1.0,
                5.0,
                1.0,
                5.0,
                -1.0,
                -5.0,
                1.0,
                -5.0,
              ]),
            }),
          },
          indices: new Uint16Array([0, 2, 1, 1, 2, 3]),
          primitiveType: PrimitiveType.TRIANGLES,
          geometryType: GeometryType.POLYLINES,
        }),
      });
      GeometryPipeline.splitLongitude(instance);
      var geometry = instance.geometry;

      expect(geometry.indices).toEqual([0, 2, 1, 1, 2, 3]);

      var positions = geometry.attributes.position.values;
      var nextPositions = geometry.attributes.nextPosition.values;
      var prevPositions = geometry.attributes.prevPosition.values;

      expect(positions).toEqual([
        -1.0,
        1.0,
        2.0,
        -1.0,
        1.0,
        2.0,
        -1.0,
        CesiumMath.EPSILON6,
        0.0,
        -1.0,
        CesiumMath.EPSILON6,
        0.0,
      ]);
      expect(nextPositions).toEqual([
        -1.0,
        CesiumMath.EPSILON6,
        0.0,
        -1.0,
        CesiumMath.EPSILON6,
        0.0,
        -1.0,
        CesiumMath.EPSILON6,
        0.0,
        -1.0,
        CesiumMath.EPSILON6,
        0.0,
      ]);
      expect(prevPositions).toEqual([
        -2.0,
        2.0,
        2.0,
        -1.0,
        2.0,
        2.0,
        -1.0,
        1.0,
        2.0,
        -1.0,
        1.0,
        2.0,
      ]);
    });

    it("returns offset wide line with first point is west and the second is on the IDL", function () {
      var instance = new GeometryInstance({
        geometry: new Geometry({
          attributes: {
            position: new GeometryAttribute({
              componentDatatype: ComponentDatatype.DOUBLE,
              componentsPerAttribute: 3,
              values: new Float64Array([
                -1.0,
                -1.0,
                2.0,
                -1.0,
                -1.0,
                2.0,
                -1.0,
                0.0,
                0.0,
                -1.0,
                0.0,
                0.0,
              ]),
            }),
            nextPosition: new GeometryAttribute({
              componentDatatype: ComponentDatatype.DOUBLE,
              componentsPerAttribute: 3,
              values: new Float64Array([
                -1.0,
                0.0,
                0.0,
                -1.0,
                0.0,
                0.0,
                -1.0,
                1.0,
                2.0,
                -1.0,
                1.0,
                2.0,
              ]),
            }),
            prevPosition: new GeometryAttribute({
              componentDatatype: ComponentDatatype.DOUBLE,
              componentsPerAttribute: 3,
              values: new Float64Array([
                -2.0,
                -2.0,
                2.0,
                -1.0,
                -2.0,
                2.0,
                -1.0,
                -1.0,
                2.0,
                -1.0,
                -1.0,
                2.0,
              ]),
            }),
            expandAndWidth: new GeometryAttribute({
              componentDatatype: ComponentDatatype.FLOAT,
              componentsPerAttribute: 2,
              values: new Float32Array([
                -1.0,
                5.0,
                1.0,
                5.0,
                -1.0,
                -5.0,
                1.0,
                -5.0,
              ]),
            }),
          },
          indices: new Uint16Array([0, 2, 1, 1, 2, 3]),
          primitiveType: PrimitiveType.TRIANGLES,
          geometryType: GeometryType.POLYLINES,
        }),
      });
      GeometryPipeline.splitLongitude(instance);
      var geometry = instance.geometry;

      expect(geometry.indices).toEqual([0, 2, 1, 1, 2, 3]);

      var positions = geometry.attributes.position.values;
      var nextPositions = geometry.attributes.nextPosition.values;
      var prevPositions = geometry.attributes.prevPosition.values;

      expect(positions).toEqual([
        -1.0,
        -1.0,
        2.0,
        -1.0,
        -1.0,
        2.0,
        -1.0,
        -CesiumMath.EPSILON6,
        0.0,
        -1.0,
        -CesiumMath.EPSILON6,
        0.0,
      ]);
      expect(nextPositions).toEqual([
        -1.0,
        -CesiumMath.EPSILON6,
        0.0,
        -1.0,
        -CesiumMath.EPSILON6,
        0.0,
        -1.0,
        -CesiumMath.EPSILON6,
        0.0,
        -1.0,
        -CesiumMath.EPSILON6,
        0.0,
      ]);
      expect(prevPositions).toEqual([
        -2.0,
        -2.0,
        2.0,
        -1.0,
        -2.0,
        2.0,
        -1.0,
        -1.0,
        2.0,
        -1.0,
        -1.0,
        2.0,
      ]);
    });

    it("returns the same points if the wide line doesn't cross the international date line", function () {
      var instance = new GeometryInstance({
        geometry: new Geometry({
          attributes: {
            position: new GeometryAttribute({
              componentDatatype: ComponentDatatype.DOUBLE,
              componentsPerAttribute: 3,
              values: new Float64Array([
                -1.0,
                1.0,
                0.0,
                -1.0,
                1.0,
                0.0,
                -1.0,
                1.0,
                2.0,
                -1.0,
                1.0,
                2.0,
              ]),
            }),
            nextPosition: new GeometryAttribute({
              componentDatatype: ComponentDatatype.DOUBLE,
              componentsPerAttribute: 3,
              values: new Float64Array([
                -1.0,
                1.0,
                2.0,
                -1.0,
                1.0,
                2.0,
                -1.0,
                2.0,
                3.0,
                -1.0,
                2.0,
                3.0,
              ]),
            }),
            prevPosition: new GeometryAttribute({
              componentDatatype: ComponentDatatype.DOUBLE,
              componentsPerAttribute: 3,
              values: new Float64Array([
                -1.0,
                -2.0,
                -1.0,
                -1.0,
                -2.0,
                -1.0,
                -1.0,
                0.0,
                0.0,
                -1.0,
                0.0,
                0.0,
              ]),
            }),
            expandAndWidth: new GeometryAttribute({
              componentDatatype: ComponentDatatype.FLOAT,
              componentsPerAttribute: 2,
              values: new Float32Array([
                -1.0,
                5.0,
                1.0,
                5.0,
                -1.0,
                -5.0,
                1.0,
                -5.0,
              ]),
            }),
          },
          indices: new Uint16Array([0, 2, 1, 1, 2, 3]),
          primitiveType: PrimitiveType.TRIANGLES,
          geometryType: GeometryType.POLYLINES,
        }),
      });
      GeometryPipeline.splitLongitude(instance);
      var geometry = instance.geometry;

      expect(geometry.indices).toEqual([0, 2, 1, 1, 2, 3]);

      var positions = geometry.attributes.position.values;
      expect(positions).toEqual([
        -1.0,
        1.0,
        0.0,
        -1.0,
        1.0,
        0.0,
        -1.0,
        1.0,
        2.0,
        -1.0,
        1.0,
        2.0,
      ]);
    });
  });

  it("splitLongitude throws when geometry is undefined", function () {
    expect(function () {
      return GeometryPipeline.splitLongitude();
    }).toThrowDeveloperError();
  });
});
