// import { Vec3 } from '../math/Vec3';
// var Edge = require('./edge');
// var martinez = require('martinez-polygon-clipping');
// var utils = require('./utils');


// var isArray = utils.isArray;
// var equals = utils.equals;
// var orientRings = utils.orientRings;


// /**
//  * Offset builder
//  *
//  * @param {Array.<Object>=} vertices
//  * @param {Number=}        arcSegments
//  * @constructor
//  */
// export class Offset {
//   vertices: null;
//   edges: null;
//   _closed: boolean;
//   _distance: number;
//   _arcSegments: any;
//   constructor(vertices: Vec3[], arcSegments: any) {

//     /**
//      * @type {Array.<Object>}
//      */
//     this.vertices = null;

//     /**
//      * @type {Array.<Edge>}
//      */
//     this.edges = null;

//     /**
//      * @type {Boolean}
//      */
//     this._closed = false;


//     /**
//      * @type {Number}
//      */
//     this._distance = 0;

//     if (vertices) {
//       this.data(vertices);
//     }

//     /**
//      * Segments in edge bounding arches
//      * @type {Number}
//      */
//     this._arcSegments = arcSegments !== undefined ? arcSegments : 5;
//   }

//   /**
//    * Change data set
//    * @param  {Array.<Array>} vertices
//    * @return {Offset}
//    */
//   data(vertices: any[] | null) {
//     this._edges = [];
//     if (!isArray(vertices)) {
//       throw new Error('Offset requires at least one coodinate to work with');
//     }

//     if (isArray(vertices) && typeof vertices[0] === 'number') {
//       this.vertices = vertices;
//     } else {
//       this.vertices = orientRings(vertices);
//       this._processContour(this.vertices, this._edges);
//     }

//     return this;
//   }


//   /**
//    * Recursively process contour to create normals
//    * @param  {*} contour
//    * @param  {Array} edges
//    */
//   _processContour(contour: string | any[] | null, edges: any[]) {
//     var i, len;
//     if (isArray(contour[0]) && typeof contour[0][0] === 'number') {
//       len = contour.length;
//       if (equals(contour[0], contour[len - 1])) {
//         len -= 1; // otherwise we get division by zero in normals
//       }
//       for (i = 0; i < len; i++) {
//         edges.push(new Edge(contour[i], contour[(i + 1) % len]));
//       }
//     } else {
//       for (i = 0, len = contour.length; i < len; i++) {
//         edges.push([]);
//         this._processContour(contour[i], edges[edges.length - 1]);
//       }
//     }
//   }


//   /**
//    * @param  {Number} arcSegments
//    * @return {Offset}
//    */
//   arcSegments(arcSegments: any) {
//     this._arcSegments = arcSegments;
//     return this;
//   }


//   /**
//    * Validates if the first and last points repeat
//    * TODO: check CCW
//    *
//    * @param  {Array.<Object>} vertices
//    */
//   validate(vertices: string | any[]) {
//     var len = vertices.length;
//     if (typeof vertices[0] === 'number') return [vertices];
//     if (vertices[0][0] === vertices[len - 1][0] &&
//       vertices[0][1] === vertices[len - 1][1]) {
//       if (len > 1) {
//         vertices = vertices.slice(0, len - 1);
//         this._closed = true;
//       }
//     }
//     return vertices;
//   }


//   /**
//    * Creates arch between two edges
//    *
//    * @param  {Array.<Object>} vertices
//    * @param  {Object}         center
//    * @param  {Number}         radius
//    * @param  {Object}         startVertex
//    * @param  {Object}         endVertex
//    * @param  {Number}         segments
//    * @param  {Boolean}        outwards
//    */
//   createArc(vertices: any[], center: number[], radius: number, startVertex: number[],
//     endVertex: number[], segments: number, outwards: boolean) {

//     var PI2 = Math.PI * 2,
//       startAngle = Math.atan2(startVertex[1] - center[1], startVertex[0] - center[0]),
//       endAngle = Math.atan2(endVertex[1] - center[1], endVertex[0] - center[0]);

//     // odd number please
//     if (segments % 2 === 0) {
//       segments -= 1;
//     }

//     if (startAngle < 0) {
//       startAngle += PI2;
//     }

//     if (endAngle < 0) {
//       endAngle += PI2;
//     }

//     var angle = ((startAngle > endAngle) ?
//       (startAngle - endAngle) :
//       (startAngle + PI2 - endAngle)),
//       segmentAngle = ((outwards) ? -angle : PI2 - angle) / segments;

//     vertices.push(startVertex);
//     for (var i = 1; i < segments; ++i) {
//       angle = startAngle + segmentAngle * i;
//       vertices.push([
//         center[0] + Math.cos(angle) * radius,
//         center[1] + Math.sin(angle) * radius
//       ]);
//     }
//     vertices.push(endVertex);
//     return vertices;
//   }


//   /**
//    * @param  {Number}  dist
//    * @param  {String=} units
//    * @return {Offset}
//    */
//   distance(dist: number, units: undefined) {
//     this._distance = dist || 0;
//     return this;
//   }


//   /**
//    * @static
//    * @param  {Number}  degrees
//    * @param  {String=} units
//    * @return {Number}
//    */
//   degreesToUnits(degrees: number, units: any) {
//     switch (units) {
//       case 'miles':
//         degrees = degrees / 69.047;
//         break;
//       case 'feet':
//         degrees = degrees / 364568.0;
//         break;
//       case 'kilometers':
//         degrees = degrees / 111.12;
//         break;
//       case 'meters':
//       case 'metres':
//         degrees = degrees / 111120.0;
//         break;
//       case 'degrees':
//       case 'pixels':
//       default:
//         break;
//     }
//     return degrees;
//   }


//   /**
//    * @param  {Array.<Object>} vertices
//    * @return {Array.<Object>}
//    */
//   ensureLastPoint(vertices: any[] | null) {
//     if (!equals(vertices[0], vertices[vertices.length - 1])) {
//       vertices.push([
//         vertices[0][0],
//         vertices[0][1]
//       ]);
//     }
//     return vertices;
//   }


//   /**
//    * Decides by the sign if it's a padding or a margin
//    *
//    * @param  {Number} dist
//    * @return {Array.<Object>}
//    */
//   offset(dist: any) {
//     this.distance(dist);
//     return this._distance === 0 ? this.vertices :
//       (this._distance > 0 ? this.margin(this._distance) :
//         this.padding(-this._distance));
//   };


//   /**
//    * @param  {Array.<Array.<Number>>} vertices
//    * @param  {Array.<Number>}         pt1
//    * @param  {Array.<Number>}         pt2
//    * @param  {Number}                 dist
//    * @return {Array.<Array.<Number>>}
//    */
//   _offsetSegment(v1: any, v2: any, e1: { offset: (arg0: number, arg1: number) => any; _inNormal: number[]; inverseOffset: (arg0: number, arg1: number) => any; _outNormal: number[]; }, dist: number) {
//     var vertices: never[] = [];
//     var offsets = [
//       e1.offset(e1._inNormal[0] * dist, e1._inNormal[1] * dist),
//       e1.inverseOffset(e1._outNormal[0] * dist, e1._outNormal[1] * dist)
//     ];

//     for (var i = 0, len = 2; i < len; i++) {
//       var thisEdge = offsets[i],
//         prevEdge = offsets[(i + len - 1) % len];
//       this.createArc(
//         vertices,
//         i === 0 ? v1 : v2, // edges[i].current, // p1 or p2
//         dist,
//         prevEdge.next,
//         thisEdge.current,
//         this._arcSegments,
//         true
//       );
//     }

//     return vertices;
//   };


//   /**
//    * @param  {Number} dist
//    * @return {Array.<Number>}
//    */
//   margin(dist: number) {
//     this.distance(dist);

//     if (typeof this.vertices[0] === 'number') { // point
//       return this.offsetPoint(this._distance);
//     }

//     if (dist === 0) return this.vertices;

//     var union = this.offsetLines(this._distance);
//     //return union;
//     union = martinez.union(this.vertices, union);
//     return orientRings(union);
//   };


//   /**
//    * @param  {Number} dist
//    * @return {Array.<Number>}
//    */
//   padding(dist: number) {
//     this.distance(dist);

//     if (this._distance === 0) return this.ensureLastPoint(this.vertices);
//     if (this.vertices.length === 2 && typeof this.vertices[0] === 'number') {
//       return this.vertices;
//     }

//     var union = this.offsetLines(this._distance);
//     var diff = martinez.diff(this.vertices, union);
//     return orientRings(diff);
//   };


//   /**
//    * Creates margin polygon
//    * @param  {Number} dist
//    * @return {Array.<Object>}
//    */
//   offsetLine(dist: number) {
//     if (dist === 0) return this.vertices;
//     return orientRings(this.offsetLines(dist));
//   };


//   /**
//    * Just offsets lines, no fill
//    * @param  {Number} dist
//    * @return {Array.<Array.<Array.<Number>>>}
//    */
//   offsetLines(dist: number) {
//     if (dist < 0) throw new Error('Cannot apply negative margin to the line');
//     var union;
//     this.distance(dist);
//     if (isArray(this.vertices[0]) && typeof this.vertices[0][0] !== 'number') {
//       for (var i = 0, len = this._edges.length; i < len; i++) {
//         union = (i === 0) ?
//           this.offsetContour(this.vertices[i], this._edges[i]) :
//           martinez.union(union, this.offsetContour(this.vertices[i], this._edges[i]));
//       }
//     } else {
//       union = (this.vertices.length === 1) ?
//         this.offsetPoint() :
//         this.offsetContour(this.vertices, this._edges);
//     }

//     return union;
//   }


//   /**
//    * @param  {Array.<Array.<Number>>|Array.<Array.<...>>} curve
//    * @param  {Array.<Edge>|Array.<Array.<...>>} edges
//    * @return {Polygon}
//    */
//   offsetContour(curve: string | any[] | null, edges: string | any[]) {
//     var union, i, len;
//     if (isArray(curve[0]) && typeof curve[0][0] === 'number') {
//       // we have 1 less edge than vertices
//       for (i = 0, len = curve.length - 1; i < len; i++) {
//         var segment = this.ensureLastPoint(
//           this._offsetSegment(curve[i], curve[i + 1], edges[i], this._distance)
//         );
//         union = (i === 0) ?
//           [this.ensureLastPoint(segment)] :
//           martinez.union(union, this.ensureLastPoint(segment));
//       }
//     } else {
//       for (i = 0, len = edges.length; i < len; i++) {
//         union = (i === 0) ?
//           this.offsetContour(curve[i], edges[i]) :
//           martinez.union(union, this.offsetContour(curve[i], edges[i]));
//       }
//     }
//     return union;
//   };


//   /**
//    * @param  {Number} distance
//    * @return {Array.<Array.<Number>}
//    */
//   offsetPoint(distance: number | undefined) {
//     this.distance(distance);
//     var vertices = this._arcSegments * 2;
//     var points = [];
//     var center = this.vertices;
//     var radius = this._distance;
//     var angle = 0;

//     if (vertices % 2 === 0) vertices++;

//     for (var i = 0; i < vertices; i++) {
//       angle += (2 * Math.PI / vertices); // counter-clockwise
//       points.push([
//         center[0] + (radius * Math.cos(angle)),
//         center[1] + (radius * Math.sin(angle))
//       ]);
//     }

//     return orientRings([this.ensureLastPoint(points)]);
//   }

// }
