chromium/third_party/google-closure-library/closure/goog/math/path_test.js

/**
 * @license
 * Copyright The Closure Library Authors.
 * SPDX-License-Identifier: Apache-2.0
 */

goog.module('goog.math.PathTest');
goog.setTestOnly();

const AffineTransform = goog.require('goog.math.AffineTransform');
const Path = goog.require('goog.math.Path');
const testSuite = goog.require('goog.testing.testSuite');

/**
 * Array mapping numeric segment constant to a descriptive character.
 * @type {!Array<string>}
 */
const SEGMENT_NAMES = function() {
  const arr = [];
  arr[Path.Segment.MOVETO] = 'M';
  arr[Path.Segment.LINETO] = 'L';
  arr[Path.Segment.CURVETO] = 'C';
  arr[Path.Segment.ARCTO] = 'A';
  arr[Path.Segment.CLOSE] = 'X';
  return arr;
}();

/**
 * Test if the given path matches the expected array of commands and parameters.
 * @param {Array<string|number>} expected The expected array of commands and
 *     parameters.
 * @param {Path} path The path to test against.
 */
const assertPathEquals = (expected, path) => {
  const actual = [];
  path.forEachSegment((seg, args) => {
    actual.push(SEGMENT_NAMES[seg]);
    Array.prototype.push.apply(actual, args);
  });
  assertEquals(expected.length, actual.length);
  for (let i = 0; i < expected.length; i++) {
    if (typeof expected[i] === 'number') {
      assertTrue(typeof actual[i] === 'number');
      assertRoughlyEquals(expected[i], actual[i], 0.01);
    } else {
      assertEquals(expected[i], actual[i]);
    }
  }
};

testSuite({
  testConstructor() {
    const path = new Path();
    assertTrue(path.isSimple());
    assertNull(path.getCurrentPoint());
    assertPathEquals([], path);
  },

  testGetSegmentCount() {
    assertArrayEquals([2, 2, 6, 6, 0], [
      Path.Segment.MOVETO, Path.Segment.LINETO, Path.Segment.CURVETO,
      Path.Segment.ARCTO, Path.Segment.CLOSE
    ].map(Path.getSegmentCount));
  },

  testSimpleMoveTo() {
    const path = new Path();
    path.moveTo(30, 50);
    assertTrue(path.isSimple());
    assertObjectEquals([30, 50], path.getCurrentPoint());
    assertPathEquals(['M', 30, 50], path);
  },

  testRepeatedMoveTo() {
    const path = new Path();
    path.moveTo(30, 50);
    path.moveTo(40, 60);
    assertTrue(path.isSimple());
    assertObjectEquals([40, 60], path.getCurrentPoint());
    assertPathEquals(['M', 40, 60], path);
  },

  testSimpleLineTo_fromArgs() {
    const path = new Path();
    const e = assertThrows(() => {
      path.lineTo(30, 50);
    });
    assertEquals('Path cannot start with lineTo', e.message);
    path.moveTo(0, 0);
    path.lineTo(30, 50);
    assertTrue(path.isSimple());
    assertObjectEquals([30, 50], path.getCurrentPoint());
    assertPathEquals(['M', 0, 0, 'L', 30, 50], path);
  },

  testSimpleLineTo_fromArray() {
    const path = new Path();
    const e = assertThrows(() => {
      path.lineToFromArray([30, 50]);
    });
    assertEquals('Path cannot start with lineTo', e.message);
    path.moveTo(0, 0);
    path.lineToFromArray([30, 50]);
    assertTrue(path.isSimple());
    assertObjectEquals([30, 50], path.getCurrentPoint());
    assertPathEquals(['M', 0, 0, 'L', 30, 50], path);
  },

  testMultiArgLineTo_fromArgs() {
    const path = new Path();
    path.moveTo(0, 0);
    path.lineTo(30, 50, 40, 60);
    assertTrue(path.isSimple());
    assertObjectEquals([40, 60], path.getCurrentPoint());
    assertPathEquals(['M', 0, 0, 'L', 30, 50, 40, 60], path);
  },

  testMultiArgLineTo_fromArray() {
    const path = new Path();
    path.moveTo(0, 0);
    path.lineToFromArray([30, 50, 40, 60]);
    assertTrue(path.isSimple());
    assertObjectEquals([40, 60], path.getCurrentPoint());
    assertPathEquals(['M', 0, 0, 'L', 30, 50, 40, 60], path);
  },

  testRepeatedLineTo_fromArgs() {
    const path = new Path();
    path.moveTo(0, 0);
    path.lineTo(30, 50);
    path.lineTo(40, 60);
    assertTrue(path.isSimple());
    assertObjectEquals([40, 60], path.getCurrentPoint());
    assertPathEquals(['M', 0, 0, 'L', 30, 50, 40, 60], path);
  },

  testRepeatedLineTo_fromArray() {
    const path = new Path();
    path.moveTo(0, 0);
    path.lineToFromArray([30, 50]);
    path.lineToFromArray([40, 60]);
    assertTrue(path.isSimple());
    assertObjectEquals([40, 60], path.getCurrentPoint());
    assertPathEquals(['M', 0, 0, 'L', 30, 50, 40, 60], path);
  },

  testSimpleCurveTo_fromArgs() {
    const path = new Path();
    const e = assertThrows(() => {
      path.curveTo(10, 20, 30, 40, 50, 60);
    });
    assertEquals('Path cannot start with curve', e.message);
    path.moveTo(0, 0);
    path.curveTo(10, 20, 30, 40, 50, 60);
    assertTrue(path.isSimple());
    assertObjectEquals([50, 60], path.getCurrentPoint());
    assertPathEquals(['M', 0, 0, 'C', 10, 20, 30, 40, 50, 60], path);
  },

  testSimpleCurveTo_fromArray() {
    const path = new Path();
    const e = assertThrows(() => {
      path.curveToFromArray([10, 20, 30, 40, 50, 60]);
    });
    assertEquals('Path cannot start with curve', e.message);
    path.moveTo(0, 0);
    path.curveToFromArray([10, 20, 30, 40, 50, 60]);
    assertTrue(path.isSimple());
    assertObjectEquals([50, 60], path.getCurrentPoint());
    assertPathEquals(['M', 0, 0, 'C', 10, 20, 30, 40, 50, 60], path);
  },

  testMultiCurveTo_fromArgs() {
    const path = new Path();
    path.moveTo(0, 0);
    path.curveTo(10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120);
    assertTrue(path.isSimple());
    assertObjectEquals([110, 120], path.getCurrentPoint());
    assertPathEquals(
        ['M', 0, 0, 'C', 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120],
        path);
  },

  testMultiCurveTo_fromArray() {
    const path = new Path();
    path.moveTo(0, 0);
    path.curveToFromArray([10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120]);
    assertTrue(path.isSimple());
    assertObjectEquals([110, 120], path.getCurrentPoint());
    assertPathEquals(
        ['M', 0, 0, 'C', 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120],
        path);
  },

  testRepeatedCurveTo_fromArgs() {
    const path = new Path();
    path.moveTo(0, 0);
    path.curveTo(10, 20, 30, 40, 50, 60);
    path.curveTo(70, 80, 90, 100, 110, 120);
    assertTrue(path.isSimple());
    assertObjectEquals([110, 120], path.getCurrentPoint());
    assertPathEquals(
        ['M', 0, 0, 'C', 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120],
        path);
  },

  testRepeatedCurveTo_fromArray() {
    const path = new Path();
    path.moveTo(0, 0);
    path.curveToFromArray([10, 20, 30, 40, 50, 60]);
    path.curveToFromArray([70, 80, 90, 100, 110, 120]);
    assertTrue(path.isSimple());
    assertObjectEquals([110, 120], path.getCurrentPoint());
    assertPathEquals(
        ['M', 0, 0, 'C', 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120],
        path);
  },

  testSimpleArc() {
    const path = new Path();
    path.arc(50, 60, 10, 20, 30, 30, false);
    assertFalse(path.isSimple());
    const p = path.getCurrentPoint();
    assertEquals(55, p[0]);
    assertRoughlyEquals(77.32, p[1], 0.01);
    assertPathEquals(['M', 58.66, 70, 'A', 10, 20, 30, 30, 55, 77.32], path);
  },

  testArcNonConnectClose() {
    const path = new Path();
    path.moveTo(0, 0);
    path.arc(10, 10, 10, 10, -90, 180, false);
    assertObjectEquals([10, 20], path.getCurrentPoint());
    path.close();
    assertObjectEquals([10, 0], path.getCurrentPoint());
  },

  testRepeatedArc() {
    const path = new Path();
    path.arc(50, 60, 10, 20, 30, 30, false);
    path.arc(50, 60, 10, 20, 60, 30, false);
    assertFalse(path.isSimple());
    assertObjectEquals([50, 80], path.getCurrentPoint());
    assertPathEquals(
        [
          'M', 58.66, 70,    'A', 10, 20, 30, 30, 55, 77.32,
          'M', 55,    77.32, 'A', 10, 20, 60, 30, 50, 80,
        ],
        path);
  },

  testRepeatedArc2() {
    const path = new Path();
    path.arc(50, 60, 10, 20, 30, 30, false);
    path.arc(50, 60, 10, 20, 60, 30, true);
    assertPathEquals(
        [
          'M',
          58.66,
          70,
          'A',
          10,
          20,
          30,
          30,
          55,
          77.32,
          'A',
          10,
          20,
          60,
          30,
          50,
          80,
        ],
        path);
  },

  testCompleteCircle() {
    const path = new Path();
    path.arc(0, 0, 10, 10, 0, 360, false);
    assertFalse(path.isSimple());
    const p = path.getCurrentPoint();
    assertRoughlyEquals(10, p[0], 0.01);
    assertRoughlyEquals(0, p[1], 0.01);
    assertPathEquals(['M', 10, 0, 'A', 10, 10, 0, 360, 10, 0], path);
  },

  testClose() {
    const path = new Path();
    assertThrows('Path cannot start with close', () => {
      path.close();
    });

    path.moveTo(0, 0);
    path.lineTo(10, 20, 30, 40, 50, 60);
    path.close();
    assertTrue(path.isSimple());
    assertObjectEquals([0, 0], path.getCurrentPoint());
    assertPathEquals(['M', 0, 0, 'L', 10, 20, 30, 40, 50, 60, 'X'], path);
  },

  testClear() {
    const path = new Path();
    path.moveTo(0, 0);
    path.arc(50, 60, 10, 20, 30, 30, false);
    path.clear();
    assertTrue(path.isSimple());
    assertNull(path.getCurrentPoint());
    assertPathEquals([], path);
  },

  testCreateSimplifiedPath() {
    let path = new Path();
    path.moveTo(0, 0);
    path.arc(50, 60, 10, 20, 30, 30, false);
    assertFalse(path.isSimple());
    path = Path.createSimplifiedPath(path);
    assertTrue(path.isSimple());
    const p = path.getCurrentPoint();
    assertEquals(55, p[0]);
    assertRoughlyEquals(77.32, p[1], 0.01);
    assertPathEquals(
        ['M', 58.66, 70, 'C', 57.78, 73.04, 56.52, 75.57, 55, 77.32], path);
  },

  testCreateSimplifiedPath2() {
    let path = new Path();
    path.arc(50, 60, 10, 20, 30, 30, false);
    path.arc(50, 60, 10, 20, 60, 30, false);
    assertFalse(path.isSimple());
    path = Path.createSimplifiedPath(path);
    assertTrue(path.isSimple());
    assertPathEquals(
        [
          'M', 58.66, 70,    'C', 57.78, 73.04, 56.52, 75.57, 55, 77.32,
          'M', 55,    77.32, 'C', 53.48, 79.08, 51.76, 80,    50, 80,
        ],
        path);
  },

  testCreateSimplifiedPath3() {
    let path = new Path();
    path.arc(50, 60, 10, 20, 30, 30, false);
    path.arc(50, 60, 10, 20, 60, 30, true);
    path.close();
    path = Path.createSimplifiedPath(path);
    assertPathEquals(
        [
          'M',
          58.66,
          70,
          'C',
          57.78,
          73.04,
          56.52,
          75.57,
          55,
          77.32,
          53.48,
          79.08,
          51.76,
          80,
          50,
          80,
          'X',
        ],
        path);
    const p = path.getCurrentPoint();
    assertRoughlyEquals(58.66, p[0], 0.01);
    assertRoughlyEquals(70, p[1], 0.01);
  },

  testArcToAsCurves() {
    const path = new Path();
    path.moveTo(58.66, 70);
    path.arcToAsCurves(10, 20, 30, 30);
    assertPathEquals(
        ['M', 58.66, 70, 'C', 57.78, 73.04, 56.52, 75.57, 55, 77.32], path);
  },

  testCreateTransformedPath() {
    const path = new Path();
    path.moveTo(0, 0);
    path.lineTo(0, 10, 10, 10, 10, 0);
    path.close();
    const tx = new AffineTransform(2, 0, 0, 3, 10, 20);
    const path2 = path.createTransformedPath(tx);
    assertPathEquals(['M', 0, 0, 'L', 0, 10, 10, 10, 10, 0, 'X'], path);
    assertPathEquals(['M', 10, 20, 'L', 10, 50, 30, 50, 30, 20, 'X'], path2);
  },

  testTransform() {
    const path = new Path();
    path.moveTo(0, 0);
    path.lineTo(0, 10, 10, 10, 10, 0);
    path.close();
    const tx = new AffineTransform(2, 0, 0, 3, 10, 20);
    const path2 = path.transform(tx);
    assertTrue(path === path2);
    assertPathEquals(['M', 10, 20, 'L', 10, 50, 30, 50, 30, 20, 'X'], path2);
  },

  testTransformCurrentAndClosePoints() {
    const path = new Path();
    path.moveTo(0, 0);
    assertObjectEquals([0, 0], path.getCurrentPoint());
    path.transform(new AffineTransform(1, 0, 0, 1, 10, 20));
    assertObjectEquals([10, 20], path.getCurrentPoint());
    path.lineTo(50, 50);
    path.close();
    assertObjectEquals([10, 20], path.getCurrentPoint());
  },

  testTransformNonSimple() {
    const path = new Path();
    path.arc(50, 60, 10, 20, 30, 30, false);
    assertThrows(() => {
      path.transform(new AffineTransform(1, 0, 0, 1, 10, 20));
    });
  },

  testAppendPath() {
    const path1 = new Path();
    path1.moveTo(0, 0);
    path1.lineTo(0, 10, 10, 10, 10, 0);
    path1.close();

    const path2 = new Path();
    path2.arc(50, 60, 10, 20, 30, 30, false);

    assertTrue(path1.isSimple());
    path1.appendPath(path2);
    assertFalse(path1.isSimple());
    assertPathEquals(
        [
          'M', 0,     0,  'L', 0,  10, 10, 10, 10, 0,     'X',
          'M', 58.66, 70, 'A', 10, 20, 30, 30, 55, 77.32,
        ],
        path1);
  },

  testIsEmpty() {
    const path = new Path();
    assertTrue('Initially path is empty', path.isEmpty());

    path.moveTo(0, 0);
    assertFalse('After command addition, path is not empty', path.isEmpty());

    path.clear();
    assertTrue('After clear, path is empty again', path.isEmpty());
  },

  testGetSegmentTypes() {
    const path = new Path();
    path.moveTo(0, 0);
    path.lineTo(10, 20, 30, 40);
    path.close();

    const Segment = Path.Segment;
    const segmentTypes = path.getSegmentTypes();
    assertArrayEquals(
        'The returned segment types do not match the expected values',
        [Segment.MOVETO, Segment.LINETO, Segment.CLOSE], segmentTypes);

    segmentTypes[2] = Segment.LINETO;
    assertArrayEquals(
        'Modifying the returned segment types changed the path',
        [Segment.MOVETO, Segment.LINETO, Segment.CLOSE],
        path.getSegmentTypes());
  },

  testGetSegmentCounts() {
    const path = new Path();
    path.moveTo(0, 0);
    path.lineTo(10, 20, 30, 40);
    path.close();

    const segmentTypes = path.getSegmentCounts();
    assertArrayEquals(
        'The returned segment counts do not match the expected values',
        [1, 2, 1], segmentTypes);

    segmentTypes[1] = 3;
    assertArrayEquals(
        'Modifying the returned segment counts changed the path', [1, 2, 1],
        path.getSegmentCounts());
  },

  testGetSegmentArgs() {
    const path = new Path();
    path.moveTo(0, 0);
    path.lineTo(10, 20, 30, 40);
    path.close();

    const segmentTypes = path.getSegmentArgs();
    assertArrayEquals(
        'The returned segment args do not match the expected values',
        [0, 0, 10, 20, 30, 40], segmentTypes);

    segmentTypes[1] = -10;
    assertArrayEquals(
        'Modifying the returned segment args changed the path',
        [0, 0, 10, 20, 30, 40], path.getSegmentArgs());
  },
});