text: var geom2d = require('@src/lib/geometry2d');
  var Drawing = require('@src/components/drawing');
  
  // various reversals of segments and endpoints that should all give identical results
  function permute(_inner, x1, y1, x2, y2, x3, y3, x4, y4, expected) {
      _inner(x1, y1, x2, y2, x3, y3, x4, y4, expected);
      _inner(x2, y2, x1, y1, x3, y3, x4, y4, expected);
      _inner(x1, y1, x2, y2, x4, y4, x3, y3, expected);
      _inner(x2, y2, x1, y1, x4, y4, x3, y3, expected);
      _inner(x3, y3, x4, y4, x1, y1, x2, y2, expected);
      _inner(x4, y4, x3, y3, x1, y1, x2, y2, expected);
      _inner(x3, y3, x4, y4, x2, y2, x1, y1, expected);
      _inner(x4, y4, x3, y3, x2, y2, x1, y1, expected);
  }
  
  describe('segmentsIntersect', function() {
      function check(x1, y1, x2, y2, x3, y3, x4, y4, expected) {
          // test swapping x/y
          var result1 = geom2d.segmentsIntersect(x1, y1, x2, y2, x3, y3, x4, y4);
          var result2 = geom2d.segmentsIntersect(y1, x1, y2, x2, y3, x3, y4, x4);
          if(Array.isArray(expected)) {
              expect(result1.x).toBeWithin(expected[0], 1e-6);
              expect(result2.y).toBeWithin(expected[0], 1e-6);
              expect(result1.y).toBeWithin(expected[1], 1e-6);
              expect(result2.x).toBeWithin(expected[1], 1e-6);
          } else {
              expect(result1).toBe(expected);
              expect(result2).toBe(expected);
          }
      }
  
      it('catches normal intersections', function() {
          permute(check, -1, -1, 1, 1, -1, 1, 1, -1, [0, 0]);
          permute(check, -1, 0, 1, 0, 0, -1, 0, 1, [0, 0]);
          permute(check, 0, 0, 100, 100, 0, 1, 100, 99, [50, 50]);
      });
  
      it('catches non-intersections', function() {
          permute(check, -1, 0, 1, 0, 0, 0.1, 0, 2, null);
          permute(check, -1, -1, 1, 1, -1, 1, 1, 2, null);
          permute(check, -1, 0, 1, 0, -1, 0.0001, 1, 0.0001, null);
          permute(check, -1, 0, 1, 0.0001, -1, 0.0001, 1, 0.00011, null);
          permute(check, -1, -1, 1, 1, -1, 0, 1, 2, null);
      });
  
      it('does not consider colinear lines intersecting', function() {
          permute(check, -1, 0, 1, 0, -1, 0, 1, 0, null);
          permute(check, -1, 0, 1, 0, -2, 0, 2, 0, null);
          permute(check, -2, -1, 2, 1, -2, -1, 2, 1, null);
          permute(check, -4, -2, 0, 0, -2, -1, 2, 1, null);
      });
  });
  
  describe('segmentDistance', function() {
      function check(x1, y1, x2, y2, x3, y3, x4, y4, expected) {
          var result1 = geom2d.segmentDistance(x1, y1, x2, y2, x3, y3, x4, y4);
          var result2 = geom2d.segmentDistance(y1, x1, y2, x2, y3, x3, y4, x4);
          expect(result1).toBeWithin(expected, 1e-6);
          expect(result2).toBeWithin(expected, 1e-6);
      }
  
      it('returns 0 if segments intersect or share endpoints', function() {
          permute(check, -1, -1, 1, 1, -1, 1, 1, -1, 0);
          permute(check, -1, 0, 1, 0, 0, -1, 0, 1, 0);
          permute(check, 0, 0, 100, 100, 0, 1, 100, 99, 0);
          permute(check, 0, 0, 1.23, 2.34, 12.99, 14.55, 1.23, 2.34, 0);
      });
  
      it('works in the endpoint-to-endpoint case', function() {
          permute(check, 0, 0, 1, 0, 5, 0, 6, 0, 4);
          permute(check, 0, 0, 1, 0, 5, 0, 6, 50, 4);
          permute(check, 0, -50, 1, 0, 5, 0, 6, 0, 4);
          permute(check, 0, -50, 1, 0, 5, 0, 6, 50, 4);
          permute(check, 1, -50, 1, 0, 5, 0, 5, 50, 4);
  
          permute(check, 0, 0, 1, 0, 2, 2, 3, 2, Math.sqrt(5));
          permute(check, 0, 0, 1, 0, 2, 2, 2, 3, Math.sqrt(5));
      });
  
      it('works in the endpoint-to-perpendicular case', function() {
          permute(check, -5, 0, 5, 0, 0, 1, 0, 2, 1);
          permute(check, -5, 0, 5, 0, 3.23, 1.55, -7.13, 1.65, 1.55);
          permute(check, 100, 0, 0, 100, 0, 5, 15, 0, 85 / Math.sqrt(2));
      });
  });
  
  describe('getVisibleSegment', function() {
      beforeAll(function() {
          Drawing.makeTester();
      });
  
      var path;
  
      beforeEach(function() {
          path = Drawing.tester.append('path').node();
      });
  
      afterEach(function() {
          path.parentNode.removeChild(path);
      });
  
      // always check against the same bounds
      var bounds = {
          left: 50,
          top: 100,
          right: 250,
          bottom: 200
      };
  
      function checkD(d, expected, msg) {
          path.setAttribute('d', d);
          [0.1, 0.3, 1, 3, 10, 30].forEach(function(buffer) {
              var msg2 = msg ? (msg + ' - ' + buffer) : buffer;
              var vis = geom2d.getVisibleSegment(path, bounds, buffer);
  
              if(!expected) {
                  expect(vis).toBeUndefined(msg2);
              } else {
                  expect(vis.min).toBeWithin(expected.min, buffer * 1.1, msg2);
                  expect(vis.max).toBeWithin(expected.max, buffer * 1.1, msg2);
                  expect(vis.len).toBeWithin(expected.len, buffer * 2.1, msg2);
                  expect(vis.total).toBeWithin(expected.total, 0.1, msg2);
                  expect(vis.isClosed).toBe(expected.isClosed, msg2);
              }
          });
      }
  
      it('returns undefined if the path is out of bounds', function() {
          checkD('M0,0V500');
          checkD('M0,0H500');
          checkD('M500,0H0');
          checkD('M0,200L99,0H201L300,200L150,201Z');
      });
  
      it('returns the whole path if it is not clipped', function() {
          var diag = 100 * Math.sqrt(5);
          checkD('M50,100L250,200', {
              min: 0, max: diag, total: diag, len: diag, isClosed: false
          });
  
          checkD('M100,110H200V185Z', {
              min: 0, max: 300, total: 300, len: 300, isClosed: true
          });
      });
  
      it('works with initial clipping', function() {
          checkD('M0,0H150V150H100', {
              min: 250, max: 350, total: 350, len: 100, isClosed: false
          });
      });
  
      it('works with both ends clipped', function() {
          checkD('M0,125H100V175H0', {
              min: 50, max: 200, total: 250, len: 150, isClosed: false
          });
      });
  
      it('works with final clipping', function() {
          checkD('M100,150H500', {
              min: 0, max: 150, total: 400, len: 150, isClosed: false
          });
      });
  
      it('is open if entry/exit points match but are not the start/end points', function() {
          checkD('M0,150H100Z', {
              min: 50, max: 150, total: 200, len: 100, isClosed: false
          });
  
          checkD('M0,150H100H50', {
              min: 50, max: 150, total: 150, len: 100, isClosed: false
          });
  
          checkD('M50,150H100H0', {
              min: 0, max: 100, total: 150, len: 100, isClosed: false
          });
      });
  
      it('can be closed even without Z', function() {
          checkD('M100,150H200H100', {
              min: 0, max: 200, total: 200, len: 200, isClosed: true
          });
  
          // notice that this one goes outside the bounds but then
          // comes back in. We don't catch that part.
          checkD('M100,150V650V150', {
              min: 0, max: 1000, total: 1000, len: 1000, isClosed: true
          });
      });
  });
  