<!-- NEW4 — TEST 4 · THE QUILT · part 299 -->
{

	/**
	 * Constructs a new shape path.
	 */
	constructor() {

		this.type = 'ShapePath';

		/**
		 * The color of the shape.
		 *
		 * @type {Color}
		 */
		this.color = new Color();

		/**
		 * The paths that have been generated for this shape.
		 *
		 * @type {Array<Path>}
		 * @default null
		 */
		this.subPaths = [];

		/**
		 * The current path that is being generated.
		 *
		 * @type {?Path}
		 * @default null
		 */
		this.currentPath = null;

		/**
		 * An object that can be used to store custom data about the shape path.
		 * Mainly used by SVGLoader to store style information.
		 *
		 * @type {Object}
		 */
		this.userData = {};

	}

	/**
	 * Creates a new path and moves it current point to the given one.
	 *
	 * @param {number} x - The x coordinate.
	 * @param {number} y - The y coordinate.
	 * @return {ShapePath} A reference to this shape path.
	 */
	moveTo( x, y ) {

		this.currentPath = new Path();
		this.subPaths.push( this.currentPath );
		this.currentPath.moveTo( x, y );

		return this;

	}

	/**
	 * Adds an instance of {@link LineCurve} to the path by connecting
	 * the current point with the given one.
	 *
	 * @param {number} x - The x coordinate of the end point.
	 * @param {number} y - The y coordinate of the end point.
	 * @return {ShapePath} A reference to this shape path.
	 */
	lineTo( x, y ) {

		this.currentPath.lineTo( x, y );

		return this;

	}

	/**
	 * Adds an instance of {@link QuadraticBezierCurve} to the path by connecting
	 * the current point with the given one.
	 *
	 * @param {number} aCPx - The x coordinate of the control point.
	 * @param {number} aCPy - The y coordinate of the control point.
	 * @param {number} aX - The x coordinate of the end point.
	 * @param {number} aY - The y coordinate of the end point.
	 * @return {ShapePath} A reference to this shape path.
	 */
	quadraticCurveTo( aCPx, aCPy, aX, aY ) {

		this.currentPath.quadraticCurveTo( aCPx, aCPy, aX, aY );

		return this;

	}

	/**
	 * Adds an instance of {@link CubicBezierCurve} to the path by connecting
	 * the current point with the given one.
	 *
	 * @param {number} aCP1x - The x coordinate of the first control point.
	 * @param {number} aCP1y - The y coordinate of the first control point.
	 * @param {number} aCP2x - The x coordinate of the second control point.
	 * @param {number} aCP2y - The y coordinate of the second control point.
	 * @param {number} aX - The x coordinate of the end point.
	 * @param {number} aY - The y coordinate of the end point.
	 * @return {ShapePath} A reference to this shape path.
	 */
	bezierCurveTo( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY ) {

		this.currentPath.bezierCurveTo( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY );

		return this;

	}

	/**
	 * Adds an instance of {@link SplineCurve} to the path by connecting
	 * the current point with the given list of points.
	 *
	 * @param {Array<Vector2>} pts - An array of points in 2D space.
	 * @return {ShapePath} A reference to this shape path.
	 */
	splineThru( pts ) {

		this.currentPath.splineThru( pts );

		return this;

	}

	/**
	 * Converts the paths into an array of shapes.
	 *
	 * @return {Array<Shape>} An array of shapes.
	 */
	toShapes() {

		// Point-in-polygon test using the even-odd ray-casting rule. Valid for
		// simple (non self-intersecting) polygons.
		function pointInPolygon( p, polygon ) {

			let inside = false;
			const n = polygon.length;

			for ( let i = 0, j = n - 1; i < n; j = i ++ ) {

				const a = polygon[ i ];
				const b = polygon[ j ];

				if ( ( a.y > p.y ) !== ( b.y > p.y ) &&
							p.x < ( b.x - a.x ) * ( p.y - a.y ) / ( b.y - a.y ) + a.x ) {

					inside = ! inside;

				}

			}

			return inside;

		}

		// Returns a point guaranteed to be strictly inside the given simple
		// polygon. First tries the bounding-box center; if that falls outside
		// the polygon, casts a horizontal ray at the center's y and picks the
		// midpoint between the first two sorted intercepts.
		//
		// Port of paper.js' Path#getInteriorPoint()
		// https://github.com/paperjs/paper.js/blob/develop/src/path/PathItem.Boolean.js
		function getInteriorPoint( polygon, boundingBox ) {

			const point = boundingBox.getCenter( new Vector2() );

			if ( pointInPolygon( point, polygon ) ) return point;

			const y = point.y;
			const intercepts = [];
			const n = polygon.length;

			for ( let i = 0; i < n; i ++ ) {

				const a = polygon[ i ];
				const b = polygon[ ( i + 1 ) % n ];

				// Half-open crossing rule — counts each vertex exactly once and
				// skips horizontal edges.
				if ( ( a.y > y ) !== ( b.y > y ) ) {

					const x = a.x + ( y - a.y ) * ( b.x - a.x ) / ( b.y - a.y );
					intercepts.push( x );

				}

			}

			if ( intercepts.length > 1 ) {

				intercepts.sort( ( a, b ) => a - b );
				point.x = ( intercepts[ 0 ] + intercepts[ 1 ] ) / 2;

			}

			return point;

		}

		// Resolve fill-rule. Defaults to 'nonzero'.
		let fillRule = ( this.userData.style && this.user