flatten-js
Version:
Javascript library for 2d geometry
326 lines (321 loc) • 18.4 kB
JavaScript
'use strict';
let expect = require('chai').expect;
let Flatten = require('../index');
// let Flatten = require('../dist/flatten.min');
let {Point, Vector, Circle, Line, Segment, Arc, Box, Polygon, Edge, Face, Ray} = Flatten;
let {point, vector, circle, line, segment, arc, ray} = Flatten;
describe('#Flatten.Arc', function() {
it('May create new instance of Arc', function () {
let arc = new Flatten.Arc();
expect(arc).to.be.an.instanceof(Flatten.Arc);
});
it('Default constructor constructs full circle unit arc with zero center and sweep 2PI CCW', function() {
let arc = new Flatten.Arc();
expect(arc.pc).to.deep.equal({x: 0, y: 0});
expect(arc.sweep).to.equal(Flatten.PIx2);
expect(arc.counterClockwise).to.equal(Flatten.CCW);
});
it('Constructor creates CCW arc if parameter counterClockwise is omitted', function () {
let arc = new Flatten.Arc(new Flatten.Point(), 1, Math.PI/4, 3*Math.PI/4);
expect(arc.sweep).to.equal(Math.PI/2);
expect(arc.counterClockwise).to.equal(Flatten.CCW);
});
it('Constructor can create different CCW arcs if counterClockwise=true 1', function () {
let arc = new Flatten.Arc(new Flatten.Point(), 1, Math.PI/4, 3*Math.PI/4, Flatten.CCW);
expect(arc.sweep).to.equal(Math.PI/2);
expect(arc.counterClockwise).to.equal(Flatten.CCW);
});
it('Constructor can create different CCW arcs if counterClockwise=true 2', function () {
let arc = new Flatten.Arc(new Flatten.Point(), 1, 3*Math.PI/4, Math.PI/4, Flatten.CCW);
expect(arc.sweep).to.equal(3*Math.PI/2);
expect(arc.counterClockwise).to.equal(Flatten.CCW);
});
it('Constructor can create different CCW arcs if counterClockwise=true 3', function () {
let arc = new Flatten.Arc(new Flatten.Point(3,4), 1, Math.PI/4, -Math.PI/4, Flatten.CCW);
expect(arc.sweep).to.equal(3*Math.PI/2);
expect(arc.counterClockwise).to.equal(Flatten.CCW);
});
it('Constructor can create different CCW arcs if counterClockwise=true 4', function () {
let arc = new Flatten.Arc(new Flatten.Point(2,-2), 1, -Math.PI/4, Math.PI/4, Flatten.CCW);
expect(arc.sweep).to.equal(Math.PI/2);
expect(arc.counterClockwise).to.equal(Flatten.CCW);
});
it('Constructor can create different CW arcs if counterClockwise=false 1', function () {
let arc = new Flatten.Arc(new Flatten.Point(), 1, Math.PI/4, 3*Math.PI/4, Flatten.CW);
expect(arc.sweep).to.equal(3*Math.PI/2);
expect(arc.counterClockwise).to.equal(Flatten.CW);
});
it('Constructor can create different CW arcs if counterClockwise=false 2', function () {
let arc = new Flatten.Arc(new Flatten.Point(), 1, 3*Math.PI/4, Math.PI/4, Flatten.CW);
expect(arc.sweep).to.equal(Math.PI/2);
expect(arc.counterClockwise).to.equal(Flatten.CW);
});
it('Constructor can create different CW arcs if counterClockwise=false 3', function () {
let arc = new Flatten.Arc(new Flatten.Point(3,4), 1, Math.PI/4, -Math.PI/4, Flatten.CW);
expect(arc.sweep).to.equal(Math.PI/2);
expect(arc.counterClockwise).to.equal(Flatten.CW);
});
it('Constructor can create different CW arcs if counterClockwise=false 4', function () {
let arc = new Flatten.Arc(new Flatten.Point(2,-2), 1, -Math.PI/4, Math.PI/4, Flatten.CW);
expect(arc.sweep).to.equal(3*Math.PI/2);
expect(arc.counterClockwise).to.equal(Flatten.CW);
});
it('In order to construct full circle, set end_angle = start_angle + 2pi', function () {
let arc = new Flatten.Arc(new Flatten.Point(), 5, Math.PI, 3*Math.PI, true);
expect(arc.sweep).to.equal(2*Math.PI);
});
it('Constructor creates zero arc when end_angle = start_angle', function () {
let arc = new Flatten.Arc(new Flatten.Point(), 5, Math.PI/4, Math.PI/4, true);
expect(arc.sweep).to.equal(0);
});
it('New arc may be constructed by function call', function() {
expect(arc(point(), 5, Math.PI, 3*Math.PI, true)).to.deep.equal(new Flatten.Arc(new Flatten.Point(), 5, Math.PI, 3*Math.PI, true));
});
it('Getter arc.start returns start point', function () {
let arc = new Flatten.Arc(new Flatten.Point(), 1, -Math.PI/4, Math.PI/4, true);
expect(arc.start).to.deep.equal({x:Math.cos(-Math.PI/4),y:Math.sin(-Math.PI/4)});
});
it('Getter arc.end returns end point', function () {
let arc = new Flatten.Arc(new Flatten.Point(), 1, -Math.PI/4, Math.PI/4, true);
expect(arc.end).to.deep.equal({x:Math.cos(Math.PI/4),y:Math.sin(Math.PI/4)});
});
it('Getter arc.length returns arc length', function () {
let arc = new Flatten.Arc(new Flatten.Point(), 1, -Math.PI/4, Math.PI/4, true);
expect(arc.length).to.equal(Math.PI/2);
});
it('Getter arc.length returns arc length', function () {
let arc = new Flatten.Arc(new Flatten.Point(), 5, -Math.PI/4, Math.PI/4, false);
expect(arc.length).to.equal(5*3*Math.PI/2);
});
it('Getter arc.box returns arc bounding box, CCW case', function () {
let arc = new Flatten.Arc(new Flatten.Point(), 1, -Math.PI/4, Math.PI/4, true);
let box = arc.box;
expect(Flatten.Utils.EQ(box.xmin,Math.sqrt(2)/2)).to.equal(true);
expect(Flatten.Utils.EQ(box.ymin,-Math.sqrt(2)/2)).to.equal(true);
expect(Flatten.Utils.EQ(box.xmax,1)).to.equal(true);
expect(Flatten.Utils.EQ(box.ymax,Math.sqrt(2)/2)).to.equal(true);
});
it('Getter arc.box returns arc bounding box, CW case', function () {
let arc = new Flatten.Arc(new Flatten.Point(), 1, -Math.PI/4, Math.PI/4, false);
let box = arc.box;
expect(Flatten.Utils.EQ(box.xmin,-1)).to.equal(true);
expect(Flatten.Utils.EQ(box.ymin,-1)).to.equal(true);
expect(Flatten.Utils.EQ(box.xmax,Math.sqrt(2)/2)).to.equal(true);
expect(Flatten.Utils.EQ(box.ymax,1)).to.equal(true);
});
it('Getter arc.box returns arc bounding box, circle case', function () {
let arc = circle(point(200,200), 75).toArc(false);
let box = arc.box;
expect(Flatten.Utils.EQ(box.xmin,125)).to.equal(true);
expect(Flatten.Utils.EQ(box.ymin,125)).to.equal(true);
expect(Flatten.Utils.EQ(box.xmax,275)).to.equal(true);
expect(Flatten.Utils.EQ(box.ymax,275)).to.equal(true);
});
describe('#Flatten.Arc.breakToFunctional', function() {
it('Case 1. No intersection with axes', function () {
let arc = new Flatten.Arc(new Flatten.Point(), 1, Math.PI/6, Math.PI/3, true);
let f_arcs = arc.breakToFunctional();
expect(f_arcs.length).to.equal(1);
expect(Flatten.Utils.EQ(f_arcs[0].startAngle, arc.startAngle)).to.equal(true);
expect(Flatten.Utils.EQ(f_arcs[0].endAngle, arc.endAngle)).to.equal(true);
});
it('Case 2. One intersection, two sub arcs', function () {
let arc = new Flatten.Arc(new Flatten.Point(), 1, Math.PI/6, 3*Math.PI/4, true);
let f_arcs = arc.breakToFunctional();
expect(f_arcs.length).to.equal(2);
expect(Flatten.Utils.EQ(f_arcs[0].startAngle, arc.startAngle)).to.equal(true);
expect(Flatten.Utils.EQ(f_arcs[0].endAngle, Math.PI/2)).to.equal(true);
expect(Flatten.Utils.EQ(f_arcs[1].startAngle, Math.PI/2)).to.equal(true);
expect(Flatten.Utils.EQ(f_arcs[1].endAngle, arc.endAngle)).to.equal(true);
});
it('Case 3. One intersection, two sub arcs, CW', function () {
let arc = new Flatten.Arc(new Flatten.Point(), 1, Math.PI/6, -Math.PI/6, false);
let f_arcs = arc.breakToFunctional();
expect(f_arcs.length).to.equal(2);
expect(Flatten.Utils.EQ(f_arcs[0].startAngle, arc.startAngle)).to.equal(true);
expect(Flatten.Utils.EQ(f_arcs[0].endAngle, 0)).to.equal(true);
expect(Flatten.Utils.EQ(f_arcs[1].startAngle, 0)).to.equal(true);
expect(Flatten.Utils.EQ(f_arcs[1].endAngle, arc.endAngle)).to.equal(true);
});
it('Case 4. One intersection, start at extreme point', function () {
let arc = new Flatten.Arc(new Flatten.Point(), 1, Math.PI/2, 3*Math.PI/4, true);
let f_arcs = arc.breakToFunctional();
expect(f_arcs.length).to.equal(1);
expect(Flatten.Utils.EQ(f_arcs[0].startAngle, Math.PI/2)).to.equal(true);
expect(Flatten.Utils.EQ(f_arcs[0].endAngle, arc.endAngle)).to.equal(true);
});
it('Case 5. 2 intersections, 3 parts', function () {
let arc = new Flatten.Arc(new Flatten.Point(), 1, Math.PI/4, 5*Math.PI/4, true);
let f_arcs = arc.breakToFunctional();
expect(f_arcs.length).to.equal(3);
expect(Flatten.Utils.EQ(f_arcs[0].startAngle, arc.startAngle)).to.equal(true);
expect(Flatten.Utils.EQ(f_arcs[0].endAngle, Math.PI/2)).to.equal(true);
expect(Flatten.Utils.EQ(f_arcs[1].endAngle, Math.PI)).to.equal(true);
expect(Flatten.Utils.EQ(f_arcs[2].endAngle, arc.endAngle)).to.equal(true);
});
it('Case 6. 2 intersections, 3 parts, CW', function () {
let arc = new Flatten.Arc(new Flatten.Point(), 1, 3*Math.PI/4, -Math.PI/4, false);
let f_arcs = arc.breakToFunctional();
expect(f_arcs.length).to.equal(3);
expect(Flatten.Utils.EQ(f_arcs[0].startAngle, arc.startAngle)).to.equal(true);
expect(Flatten.Utils.EQ(f_arcs[0].endAngle, Math.PI/2)).to.equal(true);
expect(Flatten.Utils.EQ(f_arcs[1].startAngle, Math.PI/2)).to.equal(true);
expect(Flatten.Utils.EQ(f_arcs[1].endAngle, 0)).to.equal(true);
expect(Flatten.Utils.EQ(f_arcs[2].startAngle, 0)).to.equal(true);
expect(Flatten.Utils.EQ(f_arcs[2].endAngle, arc.endAngle)).to.equal(true);
});
it('Case 7. 2 intersections on extreme points, 1 parts, CW', function () {
let arc = new Flatten.Arc(new Flatten.Point(), 1, Math.PI/2, 0, false);
let f_arcs = arc.breakToFunctional();
expect(f_arcs.length).to.equal(1);
expect(Flatten.Utils.EQ(f_arcs[0].startAngle, Math.PI/2)).to.equal(true);
expect(Flatten.Utils.EQ(f_arcs[0].endAngle, 0)).to.equal(true);
});
it('Case 7. 4 intersections on extreme points, 5 parts', function () {
let arc = new Flatten.Arc(new Flatten.Point(), 1, Math.PI/3, Math.PI/6, true);
let f_arcs = arc.breakToFunctional();
expect(f_arcs.length).to.equal(5);
expect(Flatten.Utils.EQ(f_arcs[0].startAngle, arc.startAngle)).to.equal(true);
expect(Flatten.Utils.EQ(f_arcs[4].endAngle, arc.endAngle)).to.equal(true);
});
it('Case 8. Full circle, 4 intersections on extreme points, 4 parts', function () {
let arc = new Flatten.Arc(new Flatten.Point(), 1, Math.PI/2, Math.PI/2 + 2*Math.PI, true);
let f_arcs = arc.breakToFunctional();
expect(f_arcs.length).to.equal(4);
});
});
describe('#Flatten.Arc.intersect', function() {
it('Intersect arc with segment', function() {
let arc = new Arc(point(), 1, 0, Math.PI, true);
let segment = new Segment(-1, 0.5, 1, 0.5);
let ip = arc.intersect(segment);
expect(ip.length).to.equal(2);
});
it('Intersect arc with line', function () {
let line = new Flatten.Line(point(1, 0), vector(1, 0));
let arc = new Flatten.Arc(point(1, 0), 3, -Math.PI/3, Math.PI/3, Flatten.CW);
let ip = arc.intersect(line);
expect(ip.length).to.equal(2);
});
it('Intersect arc with circle, same center and radius - return two end points', function () {
let circle = new Flatten.Circle(point(1, 0), 3);
let arc = new Flatten.Arc(point(1, 0), 3, -Math.PI/3, Math.PI/3, Flatten.CW);
let ip = arc.intersect(circle);
expect(ip.length).to.equal(2);
});
it('Intersect arc with arc', function() {
let arc1 = new Arc(point(), 1, 0, Math.PI, true);
let arc2 = new Arc(point(0,1), 1, Math.PI, 2*Math.PI, true);
let ip = arc1.intersect(arc2);
expect(ip.length).to.equal(2);
});
it('Intersect arc with arc, case of touching', function () {
let arc1 = new Arc(point(), 1, 0, Math.PI, true);
let arc2 = new Arc(point(0,2), 1, -Math.PI/4, -3*Math.PI*4, false);
let ip = arc1.intersect(arc2);
expect(ip.length).to.equal(1);
expect(ip[0]).to.deep.equal({x:0,y:1});
});
it('Intersect arc with arc, overlapping case', function () {
let arc1 = new Arc(point(), 1, 0, Math.PI, true);
let arc2 = new Arc(point(), 1, -Math.PI/2, Math.PI/2, true);
let ip = arc1.intersect(arc2);
expect(ip.length).to.equal(2);
expect(ip[0].equalTo(point(1,0))).to.equal(true);
expect(ip[1].equalTo(point(0,1))).to.equal(true);
});
it('Intersect arc with arc, overlapping case, 4 points', function () {
let arc1 = new Arc(point(), 1, -Math.PI/4, 5*Math.PI/4, true);
let arc2 = new Arc(point(), 1, Math.PI/4, 3*Math.PI/4, false);
let ip = arc1.intersect(arc2);
expect(ip.length).to.equal(4);
});
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(arc.intersect(polygon).length).to.equal(1);
});
});
it('Calculate signed area under circular arc, full circle case, CCW', function() {
let arc = new Arc(point(0,1), 1, 0, 2*Math.PI, true);
let area = arc.definiteIntegral();
expect( Flatten.Utils.EQ(area, -Math.PI)).to.equal(true);
});
it('Calculate signed area under circular arc, full circle case, CW', function() {
let arc = new Arc(point(0,1), 1, 0, 2*Math.PI, false);
let area = arc.definiteIntegral();
expect( Flatten.Utils.EQ(area, Math.PI)).to.equal(true);
});
it('It can calculate tangent vector in start point, CCW case', function () {
let arc = new Arc(point(), 5, Math.PI/4, 3*Math.PI/4, Flatten.CCW);
let tangent = arc.tangentInStart();
expect(tangent.equalTo(vector(Math.cos(3*Math.PI/4), Math.sin(3*Math.PI/4)))).to.be.true;
});
it('It can calculate tangent vector in start point, CW case', function () {
let arc = new Arc(point(), 5, Math.PI/4, 3*Math.PI/4, Flatten.CW);
let tangent = arc.tangentInStart();
expect(tangent.equalTo(vector(Math.cos(7*Math.PI/4), Math.sin(7*Math.PI/4)))).to.be.true;
});
it('It can calculate tangent vector in end point, CCW case', function () {
let arc = new Arc(point(), 5, Math.PI/4, 3*Math.PI/4, Flatten.CCW);
let tangent = arc.tangentInEnd();
expect(tangent.equalTo(vector(Math.cos(Math.PI/4), Math.sin(Math.PI/4)))).to.be.true;
});
it('It can calculate tangent vector in end point, CW case', function () {
let arc = new Arc(point(), 5, Math.PI/4, 3*Math.PI/4, Flatten.CW);
let tangent = arc.tangentInEnd();
expect(tangent.equalTo(vector(Math.cos(5*Math.PI/4), Math.sin(5*Math.PI/4)))).to.be.true;
});
it('It can calculate middle point case 1 full circle', function() {
let arc = new Circle(point(), 3).toArc();
let middle = arc.middle();
expect(middle.equalTo(point(3,0))).to.be.true;
});
it('It can calculate middle point case 2 ccw', function() {
let arc = new Arc(point(), 5, Math.PI/4, 3*Math.PI/4, Flatten.CCW);
let middle = arc.middle();
expect(middle.equalTo(point(0,5))).to.be.true;
});
it('It can calculate middle point case 3 cw', function() {
let arc = new Arc(point(), 5, Math.PI/4, 3*Math.PI/4, Flatten.CW);
let middle = arc.middle();
expect(middle.equalTo(point(0,-5))).to.be.true;
});
it('It can calculate middle point case 4 cw, startAngle > endAngle', function() {
let arc = new Arc(point(), 5, Math.PI/4, -Math.PI/4, Flatten.CW);
let middle = arc.middle();
expect(middle.equalTo(point(5,0))).to.be.true;
});
it('Can reverse arc', function() {
let arc = new Arc(point(), 5, Math.PI/4, 3*Math.PI/4, Flatten.CCW);
let reversed_arc = arc.reverse();
expect(reversed_arc.counterClockwise).to.equal(Flatten.CW);
expect(Flatten.Utils.EQ(arc.sweep,reversed_arc.sweep)).to.be.true;
})
it('Method svg() without parameters creates svg string with default attributes', function() {
let arc = new Arc(point(), 5, Math.PI/4, 3*Math.PI/4, Flatten.CCW);
let svg = arc.svg();
expect(svg.search("stroke")).to.not.equal(-1);
expect(svg.search("stroke-width")).to.not.equal(-1);
expect(svg.search("fill")).to.not.equal(-1);
})
it('Method svg() with extra parameters may add additional attributes', function() {
let arc = new Arc(point(), 5, Math.PI/4, 3*Math.PI/4, Flatten.CCW);
let svg = arc.svg({id:"123",className:"name"});
expect(svg.search("stroke")).to.not.equal(-1);
expect(svg.search("stroke-width")).to.not.equal(-1);
expect(svg.search("fill")).to.not.equal(-1);
expect(svg.search("id")).to.not.equal(-1);
expect(svg.search("class")).to.not.equal(-1);
})
});