module BABYLON { class IndexedVector2 extends Vector2 { constructor(original: Vector2, public index: number) { super(original.x, original.y); } } function nearlyEqual(a: number, b: number, epsilon: number = 0.0001): boolean { if (a === b) { return true; } return Math.abs(a - b) < epsilon; } class PolygonPoints { elements = new Array(); add(originalPoints: Array): Array { var result = new Array(); originalPoints.forEach(point => { if (result.length === 0 || !(nearlyEqual(point.x, result[0].x) && nearlyEqual(point.y, result[0].y))) { var newPoint = new IndexedVector2(point, this.elements.length); result.push(newPoint); this.elements.push(newPoint); } }); return result; } computeBounds(): { min: Vector2; max: Vector2; width: number; height: number } { var lmin = new Vector2(this.elements[0].x, this.elements[0].y); var lmax = new Vector2(this.elements[0].x, this.elements[0].y); this.elements.forEach(point => { // x if (point.x < lmin.x) { lmin.x = point.x; } else if (point.x > lmax.x) { lmax.x = point.x; } // y if (point.y < lmin.y) { lmin.y = point.y; } else if (point.y > lmax.y) { lmax.y = point.y; } }); return { min: lmin, max: lmax, width: lmax.x - lmin.x, height: lmax.y - lmin.y }; } } export class Polygon { static Rectangle(xmin: number, ymin: number, xmax: number, ymax: number): Vector2[] { return [ new Vector2(xmin, ymin), new Vector2(xmax, ymin), new Vector2(xmax, ymax), new Vector2(xmin, ymax) ]; } static Circle(radius: number, cx: number = 0, cy: number = 0, numberOfSides: number = 32): Vector2[] { var result = new Array(); var angle = 0; var increment = (Math.PI * 2) / numberOfSides; for (var i = 0; i < numberOfSides; i++) { result.push(new Vector2( cx + Math.cos(angle) * radius, cy + Math.sin(angle) * radius )); angle -= increment; } return result; } static Parse(input: string): Vector2[] { var floats = input.split(/[^-+eE\.\d]+/).map(parseFloat).filter(val => (!isNaN(val))); var i: number, result = []; for (i = 0; i < (floats.length & 0x7FFFFFFE); i += 2) { result.push(new poly2tri.Point(floats[i], floats[i + 1])); } return result; } static StartingAt(x: number, y: number): Path2 { return Path2.StartingAt(x, y); } } export class PolygonMeshBuilder { private _swctx: poly2tri.SweepContext; private _points = new PolygonPoints(); private _name: string; private _scene: Scene; constructor(name: string, contours: Path2, scene: Scene) constructor(name: string, contours: Vector2[], scene: Scene) constructor(name: string, contours: any, scene: Scene) { if (!("poly2tri" in window)) { throw "PolygonMeshBuilder cannot be used because poly2tri is not referenced"; } this._name = name; this._scene = scene; var points: Vector2[]; if (contours instanceof Path2) { points = (contours).getPoints(); } else { points = (contours); } this._swctx = new poly2tri.SweepContext(this._points.add(points)); } addHole(hole: Vector2[]): PolygonMeshBuilder { this._swctx.addHole(this._points.add(hole)); return this; } build(updatable: boolean = false): Mesh { var result = new Mesh(this._name, this._scene); var normals = []; var positions = []; var uvs = []; var bounds = this._points.computeBounds(); this._points.elements.forEach((p) => { normals.push(0, 1.0, 0); positions.push(p.x, 0, p.y); uvs.push((p.x - bounds.min.x) / bounds.width, (p.y - bounds.min.y) / bounds.height); }); var indices = []; this._swctx.triangulate(); this._swctx.getTriangles().forEach((triangle) => { triangle.getPoints().forEach((point) => { indices.push((point).index); }); }); result.setVerticesData(positions, VertexBuffer.PositionKind, updatable); result.setVerticesData(normals, VertexBuffer.NormalKind, updatable); result.setVerticesData(uvs, VertexBuffer.UVKind, updatable); result.setIndices(indices); return result; } } }