UNPKG

flatten-js

Version:

Javascript library for 2d geometry

504 lines (461 loc) 19.3 kB
/** * Created by Alex Bol on 3/25/2017. */ let expect = require('chai').expect; // let Flatten = require('../index'); let Flatten = require('../dist/flatten.commonjs2'); let {Point, Vector, Circle, Line, Segment, Arc, Box, Polygon, Edge, Face, PlanarSet} = Flatten; let {point, vector, circle, line, segment, arc, box} = Flatten; describe('#Flatten.Polygon', function() { it('May create new instance of Polygon', function () { let polygon = new Polygon(); expect(polygon).to.be.an.instanceof(Polygon); }); it('Default construct creates instance of Polygon faces edges as instances of PlanarSet', function () { let polygon = new Polygon(); expect(polygon.edges).to.be.an.instanceof(PlanarSet); expect(polygon.faces).to.be.an.instanceof(PlanarSet); }); it('Default construct creates instance of Polygon with 0 faces and 0 edges', function () { let polygon = new Polygon(); expect(polygon.edges.size).to.equal(0); expect(polygon.faces.size).to.equal(0); }); it('Can construct Polygon from array of 3 points', function () { let polygon = new Polygon(); let points = [ new Point(1,1), new Point(5,1), new Point(3, 5) ]; polygon.addFace(points); expect(polygon.edges.size).to.equal(3); expect(polygon.faces.size).to.equal(1); }); it('Can construct Polygon from array of 3 segments', function () { let polygon = new Polygon(); let points = [ new Point(1,1), new Point(5,1), new Point(3, 5) ]; let segments = [ new Segment(points[0], points[1]), new Segment(points[1], points[2]), new Segment(points[2], points[0]) ]; polygon.addFace(segments); expect(polygon.edges.size).to.equal(3); expect(polygon.faces.size).to.equal(1); }); it('Can construct Polygon with multiple faces', function () { let polygon = new Polygon(); let points = [ new Point(1,1), new Point(5,1), new Point(3, 5), new Point(-1,-1), new Point(-5,-1), new Point(-3, -5), ]; let segments1 = [ new Segment(points[0], points[1]), new Segment(points[1], points[2]), new Segment(points[2], points[0]) ]; let segments2 = [ new Segment(points[3], points[4]), new Segment(points[4], points[5]), new Segment(points[5], points[3]) ]; polygon.addFace(segments1); polygon.addFace(segments2); expect(polygon.edges.size).to.equal(6); expect(polygon.faces.size).to.equal(2); }); it('Can construct polygon from Circle in CCW orientation', function() { let polygon = new Polygon(); polygon.addFace(circle(point(3,3),50)); expect(polygon.faces.size).to.equal(1); expect(polygon.edges.size).to.equal(1); expect([...polygon.faces][0].orientation()).to.equal(Flatten.ORIENTATION.CCW); }); it('Can construct polygon from a box in CCW orientation', function() { let polygon = new Polygon(); polygon.addFace(box(30,40,100,200)); expect(polygon.faces.size).to.equal(1); expect(polygon.edges.size).to.equal(4); expect([...polygon.faces][0].orientation()).to.equal(Flatten.ORIENTATION.CCW); }); it('Can construct polygon from a box and circle as a hole', function() { let polygon = new Polygon(); polygon.addFace(box(0,0,100,100)); polygon.addFace(circle(point(40,40),20)).reverse(); // change orientation to CW expect(polygon.faces.size).to.equal(2); expect(polygon.edges.size).to.equal(5); expect([...polygon.faces][0].size).to.equal(4); expect([...polygon.faces][0].orientation()).to.equal(Flatten.ORIENTATION.CCW); expect([...polygon.faces][1].size).to.equal(1); expect([...polygon.faces][1].orientation()).to.equal(Flatten.ORIENTATION.CW); }); it('Can remove faces from polygon', function () { let polygon = new Polygon(); let points = [ new Point(1,1), new Point(5,1), new Point(3, 5), new Point(-1,-1), new Point(-5,-1), new Point(-3, -5), ]; let segments1 = [ new Segment(points[0], points[1]), new Segment(points[1], points[2]), new Segment(points[2], points[0]) ]; let segments2 = [ new Segment(points[3], points[4]), new Segment(points[4], points[5]), new Segment(points[5], points[3]) ]; let face1 = polygon.addFace(segments1); let face2 = polygon.addFace(segments2); polygon.deleteFace(face1); expect(polygon.faces.has(face1)).to.be.false; expect(polygon.edges.size).to.equal(3); expect(polygon.faces.size).to.equal(1); polygon.deleteFace(face2); expect(polygon.faces.has(face2)).to.be.false; expect(polygon.edges.size).to.equal(0); expect(polygon.faces.size).to.equal(0); }); it('Can create new instance of the polygon', function() { let polygon = new Polygon(); polygon.addFace([point(1,1), point(3,1), point(3,2), point(1,2)]); polygon.addFace([point(-1,1), point(-3,1), point(-3, 2), point(-1,2)]); let polygon2 = polygon.clone(); expect(polygon2 === polygon).to.be.false; expect(polygon2).to.deep.equal(polygon); }); it('Can return bounding box of the polygon', function() { let polygon = new Polygon(); polygon.addFace([point(1,1), point(3,1), point(3,2), point(1,2)]); polygon.addFace([point(-1,1), point(-3,1), point(-3, 2), point(-1,2)]); let box = polygon.box; expect(box.xmin).to.equal(-3); expect(box.ymin).to.equal(1); expect(box.xmax).to.equal(3); expect(box.ymax).to.equal(2); }); it('Can return array of vertices', function() { let polygon = new Polygon(); let points = [point(1,1), point(3,1), point(3,2), point(1,2)]; polygon.addFace(points); expect(polygon.vertices).to.deep.equal(points); }); it('Can calculate area of the one-face polygon', function() { let polygon = new Polygon(); let face = polygon.addFace([ point(1,1), point(3,1), point(3,2), point(1,2) ]); expect(polygon.area()).to.equal(2); }); it('Can check point in contour. Donut Case 1 Boundary top',function() { let polygon = new Polygon(); let a = circle(point(200,200), 100).toArc(true); let b = circle(point(200,200), 75).toArc(false); polygon.addFace([a]); polygon.addFace([b]); let pt = point(200,100); expect(polygon.contains(pt)).to.be.true; }); it('Can check point in contour. Donut Case 2 Center',function() { let polygon = new Polygon(); let a = circle(point(200,200), 100).toArc(true); let b = circle(point(200,200), 75).toArc(false); polygon.addFace([a]); polygon.addFace([b]); let pt = point(200,200); expect(pt.on(polygon)).to.be.false; }); it('Can check point in contour. Donut Case 3 Inside',function() { let polygon = new Polygon(); let a = circle(point(200,200), 100).toArc(true); let b = circle(point(200,200), 75).toArc(false); polygon.addFace([a]); polygon.addFace([b]); let pt = point(200,290); expect(polygon.contains(pt)).to.be.true; }); it('Can check point in contour. Donut Case 4 Boundary inner circle start',function() { let polygon = new Polygon(); let a = circle(point(200,200), 100).toArc(true); let b = circle(point(200,200), 75).toArc(false); polygon.addFace([a]); polygon.addFace([b]); let pt = point(125, 200); expect(polygon.contains(pt)).to.be.true; }); it('Can measure distance between circle and polygon', function () { let points = [ point(100, 20), point(250, 75), point(350, 75), point(300, 270), point(170, 200), point(120, 350), point(70, 120) ]; let poly = new Polygon(); poly.addFace(points); poly.addFace([circle(point(175,150), 30).toArc()]); let c = circle(point(300,25), 25); let [dist, shortest_segment] = poly.distanceTo(c); expect(dist).to.equal(25); expect(shortest_segment.pe).to.deep.equal({"x": 300, "y": 50}); expect(shortest_segment.ps).to.deep.equal({"x": 300, "y": 75}); }); it('Can measure distance between two polygons', function () { "use strict"; let points = [ point(100, 20), point(250, 75), point(350, 75), point(300, 200), point(170, 200), point(120, 350), point(70, 120) ]; let poly1 = new Polygon(); poly1.addFace(points); poly1.addFace([circle(point(175,150), 30).toArc()]); let poly2 = new Polygon(); poly2.addFace( [circle(point(250, 300), 50).toArc()]); let [dist, shortest_segment] = Flatten.Distance.distance(poly1, poly2); expect(dist).to.equal(50); expect(shortest_segment.pe).to.deep.equal({"x": 250, "y": 250}); expect(shortest_segment.ps).to.deep.equal({"x": 250, "y": 200}); }); it('Can add new vertex to face and split edge of polygon (segment)', function () { "use strict"; let points = [ point(100, 20), point(200, 20), point(200, 40), point(100, 40) ]; let poly = new Polygon(); poly.addFace(points); expect(poly.edges.size).to.equal(4); let pt = point(150,20); let edge = [...poly.edges].find((e) => e.shape.contains(pt)); let newEdge = poly.addVertex(pt, edge); expect(poly.edges.size).to.equal(5); expect(edge.start).to.deep.equal(pt); expect(edge.end).to.deep.equal({x:200,y:20}); expect(newEdge.start).to.deep.equal({x:100,y:20}); expect(newEdge.end).to.deep.equal(pt); expect(newEdge.next).to.equal(edge); expect(edge.prev).to.equal(newEdge); }); it('Can add new vertex to face and split edge of polygon (arc)',function() { let polygon = new Polygon(); polygon.addFace([circle(point(200,200), 100).toArc(true)]); let pt = point(300,200); expect(pt.on(polygon)).to.be.true; let edge = [...polygon.edges].find((e) => e.shape.contains(pt)); let newEdge = polygon.addVertex(pt, edge); expect(polygon.edges.size).to.equal(2); expect(newEdge.end.equalTo(edge.start)).to.be.true; expect(edge.end.equalTo(newEdge.start)).to.be.true; expect(Flatten.Utils.EQ(newEdge.shape.sweep, Math.PI)).to.be.true; expect(Flatten.Utils.EQ(edge.shape.sweep, Math.PI)).to.be.true; expect(newEdge.next).to.equal(edge); expect(newEdge.prev).to.equal(edge); expect(edge.prev).to.equal(newEdge); expect(edge.next).to.equal(newEdge); }); it('Can calculate inclusion flag of the edge', function () { "use strict"; let points = [ point(100, 20), point(200, 20), point(200, 40), point(100, 40) ]; let poly = new Polygon(); poly.addFace(points); let bv1 = new Edge(segment(point(120,30), point(130, 35))).setInclusion(poly); // fully inside let bv2 = new Edge(segment(point(120,50), point(130, 55))).setInclusion(poly); // fully outside let bv3 = new Edge(segment(point(100,30), point(200, 30))).setInclusion(poly); // middle inside let bv4 = new Edge(segment(point(150,30), point(200, 40))).setInclusion(poly); // start inside let bv5 = new Edge(segment(point(120,20), point(130, 20))).setInclusion(poly); // boundary expect(bv1).to.equal(Flatten.INSIDE); expect(bv2).to.equal(Flatten.OUTSIDE); expect(bv3).to.equal(Flatten.INSIDE); expect(bv4).to.equal(Flatten.INSIDE); expect(bv5).to.equal(Flatten.BOUNDARY); }); it('Can remove chain of edges from face', function () { "use strict"; let points = [ point(100, 20), point(200, 20), point(200, 40), point(100, 40) ]; let poly = new Polygon(); let face = poly.addFace(points); expect(face.size).to.equal(4); // remove chain from #1 to #2, leave #0 #3 let edgeFrom = face.first; // #1 let edgeTo = edgeFrom.next; // #2 poly.removeChain(face, edgeFrom, edgeTo); expect(face.size).to.equal(2); expect(poly.edges.size).to.equal(2); }); it('Can stringify and parse polygon with segments', function () { let polygon = new Polygon(); let points = [ new Point(1,1), new Point(5,1), new Point(3, 5), new Point(-1,-1), new Point(-5,-1), new Point(-3, -5), ]; let segments1 = [ new Segment(points[0], points[1]), new Segment(points[1], points[2]), new Segment(points[2], points[0]) ]; let segments2 = [ new Segment(points[3], points[4]), new Segment(points[4], points[5]), new Segment(points[5], points[3]) ]; polygon.addFace(segments1); polygon.addFace(segments2); let string = JSON.stringify(polygon); let jsonPolygon = JSON.parse(string); let newPolygon = new Polygon(); for (let jsonFace of jsonPolygon) { newPolygon.addFace(jsonFace); } expect(newPolygon.edges.size).to.equal(polygon.edges.size); expect(newPolygon.faces.size).to.equal(polygon.faces.size); }); it('Can stringify and parse polygon with segments and arcs', function () { "use strict"; let points = [ point(100, 20), point(250, 75), point(350, 75), point(300, 200), point(170, 200), point(120, 350), point(70, 120) ]; let polygon = new Polygon(); polygon.addFace(points); polygon.addFace([circle(point(175,150), 30).toArc()]); let string = JSON.stringify(polygon,null," "); let jsonPolygon = JSON.parse(string); let newPolygon = new Polygon(); for (let jsonFace of jsonPolygon) { newPolygon.addFace(jsonFace); } expect(newPolygon.edges.size).to.equal(polygon.edges.size); expect(newPolygon.faces.size).to.equal(polygon.faces.size); }); it('Can check if polygon is valid', function () { "use strict"; let points = [ point(100, 20), point(250, 75), point(350, 75), point(300, 200), point(170, 200), point(120, 350), point(70, 120) ]; let polygon = new Polygon(); polygon.addFace(points); expect(polygon.isValid()).to.be.true; }); it('Can check if polygon is invalid if one of faces is not simple', function () { "use strict"; let points = [ point(100, 20), point(220, 270), point(350, 75), point(300, 200), point(170, 200), point(120, 350), point(70, 120) ]; let polygon = new Polygon(); polygon.addFace(points); expect(polygon.isValid()).to.be.false; }); describe('#Flatten.Polygon.intersect(shape) methods', function() { it('Intersect arc with polygon', function() { let points = [ point(100, 20), point(250, 75), point(350, 75), point(300, 200), point(170, 200), point(120, 350), point(70, 120) ]; let polygon = new Polygon(); polygon.addFace(points); let arc = new Arc(point(150,50), 50, Math.PI/3, 5*Math.PI/3, Flatten.CCW); expect(polygon.intersect(arc).length).to.equal(1); }); it('Intersect circle with polygon', function() { let points = [ point(100, 20), point(250, 75), point(350, 75), point(300, 200), point(170, 200), point(120, 350), point(70, 120) ]; let polygon = new Polygon(); polygon.addFace(points); let circle = new Circle(point(150,50), 50); expect(circle.intersect(polygon).length).to.equal(2); }); it('Line to polygon intersection', function() { "use strict"; let points = [ point(100, 20), point(250, 75), point(350, 75), point(300, 200), point(170, 200), point(120, 350), point(70, 120) ]; let polygon = new Polygon(); polygon.addFace(points); let line = new Flatten.Line(point(100, 20), point(300, 200)); expect(polygon.intersect(line).length).to.equal(4); }); it('Intersection with Polygon', function () { let segment = new Flatten.Segment(150,-20,150,60); let points = [ point(100, 20), point(200, 20), point(200, 40), point(100, 40) ]; let poly = new Polygon(); let face = poly.addFace(points); let ip_expected = new Flatten.Point(0, 2); let ip = poly.intersect(segment); expect(ip.length).to.equal(2); expect(ip[0].equalTo(point(150,20))).to.be.true; expect(ip[1].equalTo(point(150,40))).to.be.true; }); }); it('Issue #18 Division by zero error when checking if polygon contains a point',function() { const points = [ new Flatten.Point(-0.0774582, 51.4791865), new Flatten.Point(-0.0784252, 51.4792941), new Flatten.Point(-0.0774582, 51.4791865) ]; const poly = new Flatten.Polygon(); poly.addFace(points); const pp = new Flatten.Point(-0.07776044568759738, 51.47918678917519); const contains = poly.contains(pp); expect(contains).to.be.equal(false); }); });