├── .gitattributes
├── LICENSE.txt
├── README.md
├── index.css
├── index.html
├── index.js
├── solution.js
├── test.css
├── test.html
└── test.js
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
4 | # Custom for Visual Studio
5 | *.cs diff=csharp
6 |
7 | # Standard to msysgit
8 | *.doc diff=astextplain
9 | *.DOC diff=astextplain
10 | *.docx diff=astextplain
11 | *.DOCX diff=astextplain
12 | *.dot diff=astextplain
13 | *.DOT diff=astextplain
14 | *.pdf diff=astextplain
15 | *.PDF diff=astextplain
16 | *.rtf diff=astextplain
17 | *.RTF diff=astextplain
18 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2016 Oleksandr Goncharov
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # js-intersect
2 | This is demo of polygons intersection algorithm written in JS. Visual part of project based on template provided by [kottans.org](http//kottans.org).
3 | Demo site is available via GitHub Pages here: [vrd.github.io/js-intersect] (http://vrd.github.io/js-intersect).
4 |
5 | Project consists of following files:
6 | - __index.html__ - main demo page
7 | - __test.html__ - page with test results for checking algorithm correctness
8 | - __index.js__ - code for drawing polygons and calling intersects() function
9 | - __solution.js__ - script with `intersect()` function and many other functions that realize algorithm
10 | - __test.js__ - script with tests
11 | - __index.css__ - styles for index.html
12 | - __test.css__ - styles for test.html
13 |
14 | ##### How to use:
15 | Function `intersect()` in file __solution.js__ is a top-level function of an algotithm. Function takes two arguments. Each argument is a polygon given in a form of list of its vertices. Every vertex is an object with two keys: "x" and "y". Example of polygon:
16 | `[{x: 100, y: 200}, {x: 150, y: 200}, {x: 150, y: 270}]` Polygon can consist of 3 to 100 vertices.
17 | Function returns list of polygons (result of polygons intersection) in same format.
18 |
19 | P.S. Algorithm can perform not only intersection of two polygons but other actions: union, complement etc. Look at function `filterPolygons()` in file solution.js for more information.
20 |
21 |
22 |
--------------------------------------------------------------------------------
/index.css:
--------------------------------------------------------------------------------
1 | html, body {
2 | width: 100%;
3 | height: 100%;
4 | }
5 |
6 | p {
7 | max-width: 700px;
8 | }
9 |
10 | .container {
11 | border: solid lightgray 3px;
12 | width: 400px;
13 | height: 400px;
14 | position: absolute;
15 | left: 50%;
16 | top: 50%;
17 | transform: translateX(-50%) translateY(-50%);
18 | }
19 |
20 | .container svg {
21 | position: absolute;
22 | left: 0;
23 | top: 0;
24 | width: 100%;
25 | height: 100%;
26 | }
27 |
28 | svg.base polygon {
29 | stroke: black;
30 | stroke-width: 2px;
31 | opacity: 0.5;
32 | fill-rule:evenodd;
33 | }
34 |
35 | @keyframes blink {
36 | 0% { opacity: 0; }
37 | 50% { opacity: 1; }
38 | 100% { opacity: 0; }
39 | }
40 |
41 | svg.intersections {
42 | animation-name: blink;
43 | animation-duration: 1s;
44 | animation-iteration-count: infinite;
45 | }
46 |
47 | .control {
48 | position: absolute;
49 | left: 50%;
50 | top: 90%;
51 | transform: translateX(-50%) translateY(-90%);
52 | text-align:center;
53 | }
54 |
55 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Main | Intersections in JS
8 |
9 |
10 |
11 | js-intersect demo
12 |
13 | This is demo page for visualization of polygons intersection algorithm. Press "Draw random" button to see intersection of two random polygons. You can change points number of random polygons from 3 to 100 (if polygons contain many points processing may take some time).
14 | test results
15 |
16 | GitHub
17 |
18 |
19 |
20 |
21 | First polygon:
22 |
23 | Second polygon:
24 |
25 |
26 |
27 |
28 |
29 |
30 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | // пример многоугольников
2 | var examples = {
3 | first: [
4 | { x: 60, y: 60 },
5 | { x: 180, y: 0 },
6 | { x: 300, y: 60 },
7 | { x: 300, y: 300 },
8 | { x: 240, y: 180 },
9 | { x: 210, y: 180 },
10 | { x: 180, y: 240 },
11 | { x: 150, y: 180 },
12 | { x: 120, y: 180 },
13 | { x: 60, y: 300 },
14 | ],
15 | second: [
16 | { x: 300, y: 240 },
17 | { x: 330, y: 220 },
18 | { x: 330, y: 210 },
19 | { x: 270, y: 90 },
20 | { x: 230, y: 270 },
21 | { x: 210, y: 90 },
22 | { x: 180, y: 60 },
23 | { x: 150, y: 90 },
24 | { x: 140, y: 280 },
25 | { x: 90, y: 90 },
26 | { x: 30, y: 210 }
27 | ]
28 |
29 | };
30 |
31 | function drawPolygon(data, container, color) {
32 | var pol = document.createElementNS('http://www.w3.org/2000/svg', 'polygon');
33 | var str = '';
34 | str += data.map(function (point) {
35 | return point.x + ',' + point.y;
36 | }).join(' ');
37 | pol.setAttribute('points', str);
38 | pol.style.fill = color;
39 | container.appendChild(pol);
40 | }
41 |
42 |
43 | // function getScalingParameter(pols, viewport_width, viewport_height) {
44 |
45 | // }
46 |
47 | // function scalePolygon(pol, scaleCoef) {
48 | // var new_pol = [];
49 | // for (var i = 0; i < pol.length; i++) {
50 | // new_pol.push({x: pol[i].x*scaleCoef, y: pol[i].y*scaleCoef});
51 | // }
52 | // return new_pol;
53 | // }
54 |
55 | function getMoveParameters(pols, viewport_height, viewport_width) {
56 | var x_min = Infinity, x_max = -Infinity, y_min = Infinity, y_max = -Infinity;
57 | for (var i = 0; i < pols.length; i++) {
58 | for (var j = 0; j < pols[i].length; j++) {
59 | if (pols[i][j].x < x_min) x_min = pols[i][j].x;
60 | if (pols[i][j].x > x_max) x_max = pols[i][j].x;
61 | if (pols[i][j].y < y_min) y_min = pols[i][j].y;
62 | if (pols[i][j].y > y_max) y_max = pols[i][j].y;
63 | }
64 | }
65 | var center = {x:0,y:0};
66 | center.x = (x_max + x_min)/2;
67 | center.y = (y_max + y_min)/2;
68 | var shift = {x:0,y:0};
69 | shift.x = viewport_width/2 - center.x;
70 | shift.y = viewport_height/2 - center.y;
71 | return shift;
72 | }
73 |
74 | function movePolygon(pol, shiftX, shiftY) {
75 | var new_pol = [];
76 | for (var i = 0; i < pol.length; i++) {
77 | new_pol.push({x: pol[i].x+shiftX, y: pol[i].y+shiftY});
78 | }
79 | return new_pol;
80 | }
81 |
82 | function drawAllPolygons(pol1, pol2) {
83 | // pols = [pol1, pol2];
84 | // shift = getMoveParameters(pols, 500, 500);
85 | // pol1 = movePolygon(pol1, shift.x, shift.y);
86 | // pol2 = movePolygon(pol2, shift.x, shift.y);
87 | drawPolygon(pol1, document.querySelector('svg.base'), 'navy');
88 | drawPolygon(pol2, document.querySelector('svg.base'), 'yellow');
89 | var pols = [];
90 | pols = intersect(pol1, pol2);
91 | console.info("Found " + pols.length + " intersections");
92 | pols.forEach(function (p) {
93 | drawPolygon(p, document.querySelector('svg.intersections'), 'red');
94 | });
95 | }
96 |
97 | function getTwoRandomPolygons(num1, num2) {
98 | var twoPolygons = [[],[]];
99 | var x, y;
100 | nums = [num1, num2];
101 | for (var i = 0; i < nums.length; i++) {
102 | for (var j = 0; j < nums[i]; j++) {
103 | x = Math.round(380*Math.random() + 10);
104 | y = Math.round(380*Math.random() + 10);
105 | twoPolygons[i].push({x: x, y: y});
106 | }
107 | }
108 | //log(twoPolygons);
109 | return twoPolygons;
110 | }
111 |
112 | function drawDefault() {
113 | drawAllPolygons(examples.first, examples.second);
114 | }
115 |
116 | function drawRandom() {
117 | var svg1 = document.querySelector('svg.base');
118 | var base = svg1.getElementsByTagName('*');
119 | for (i = base.length - 1; i >= 0; i--) {
120 | svg1.removeChild(base[i]);
121 | }
122 | var svg2 = document.querySelector('svg.intersections');
123 | base = svg2.getElementsByTagName('*');
124 | for (i = base.length - 1; i >= 0; i--) {
125 | svg2.removeChild(base[i]);
126 | }
127 | const MAX = 100;
128 | const MIN = 3;
129 | var pol1 = document.getElementById('pol1');
130 | pol1.value = Math.round(pol1.value);
131 | if (pol1.value > MAX) pol1.value = MAX;
132 | if (pol1.value < MIN) pol1.value = MIN;
133 | var pol2 = document.getElementById('pol2');
134 | pol2.value = Math.round(pol2.value);
135 | if (pol2.value > MAX) pol2.value = MAX;
136 | if (pol2.value < MIN) pol2.value = MIN;
137 | var polygons = getTwoRandomPolygons(pol1.value, pol2.value);
138 | drawAllPolygons(polygons[0], polygons[1]);
139 |
140 | }
141 |
142 |
143 |
--------------------------------------------------------------------------------
/solution.js:
--------------------------------------------------------------------------------
1 | function intersect(fig1, fig2) {
2 | for (let i = 0; i < fig1.length; i++) {
3 | fig1[i].x = +((fig1[i].x).toFixed(9));
4 | fig1[i].y = +((fig1[i].y).toFixed(9));
5 | }
6 | for (let i = 0; i < fig2.length; i++) {
7 | fig2[i].x = +((fig2[i].x).toFixed(9));
8 | fig2[i].y = +((fig2[i].y).toFixed(9));
9 | }
10 | var fig2a = alignPolygon(fig2, fig1);
11 | if (!checkPolygons(fig1, fig2a)) {
12 | return false;
13 | }
14 | var edges = edgify(fig1, fig2a);
15 | var polygons = polygonate(edges);
16 | var filteredPolygons = filterPolygons(polygons, fig1, fig2a, "intersect");
17 | return filteredPolygons;
18 | }
19 |
20 | function alignPolygon(polygon, points) {
21 | for (let i = 0; i < polygon.length; i++) {
22 | for (let j = 0; j < points.length; j++) {
23 | if (distance(polygon[i], points[j]) < 0.00000001)
24 | polygon[i] = points[j];
25 | }
26 | }
27 | return polygon;
28 | }
29 |
30 | function distance(p1, p2) {
31 | var dx = Math.abs(p1.x - p2.x);
32 | var dy = Math.abs(p1.y - p2.y);
33 | return Math.sqrt(dx*dx + dy*dy);
34 | }
35 |
36 | //check polygons for correctness
37 | function checkPolygons(fig1, fig2) {
38 | var figs = [fig1, fig2];
39 | for (var i = 0; i < figs.length; i++) {
40 | if (figs[i].length < 3) {
41 | console.error("Polygon " + (i+1) + " is invalid!");
42 | return false;
43 | }
44 | }
45 | return true;
46 | }
47 |
48 | //create array of edges of all polygons
49 | function edgify(fig1, fig2) {
50 | //create primary array from all edges
51 | var primEdges = getEdges(fig1).concat(getEdges(fig2));
52 | var secEdges = [];
53 | //check every edge
54 | for(var i = 0; i < primEdges.length; i++) {
55 | var points = [];
56 | //for intersection with every edge except itself
57 | for(var j = 0; j < primEdges.length; j++) {
58 | if (i != j) {
59 | var interPoints = findEdgeIntersection(primEdges[i], primEdges[j]);
60 | addNewPoints(interPoints, points);
61 | }
62 | }
63 | //add start and end points to intersection points
64 | startPoint = primEdges[i][0];
65 | startPoint.t = 0;
66 | endPoint = primEdges[i][1];
67 | endPoint.t = 1;
68 | addNewPoints([startPoint, endPoint], points);
69 | //sort all points by position on edge
70 | points = sortPoints(points);
71 | //break edge to parts
72 | for (var k = 0; k < points.length - 1; k++) {
73 | var edge = [
74 | { x: points[k].x, y: points[k].y },
75 | { x: points[k+1].x, y: points[k+1].y}
76 | ];
77 | // check for existanse in sec.array
78 | if (!edgeExists(edge, secEdges)) {
79 | //push if not exists
80 | secEdges.push(edge);
81 | }
82 | }
83 | }
84 | return secEdges;
85 | }
86 |
87 | function addNewPoints(newPoints, points) {
88 | if (newPoints.length > 0) {
89 | //check for uniqueness
90 | for (var k = 0; k < newPoints.length; k++) {
91 | if (!pointExists(newPoints[k], points)) {
92 | points.push(newPoints[k]);
93 | }
94 | }
95 | }
96 | }
97 |
98 | function sortPoints(points) {
99 | var p = points;
100 | p.sort((a,b) => {
101 | if (a.t > b.t) return 1;
102 | if (a.t < b.t) return -1;
103 | });
104 | return p;
105 | }
106 |
107 | function getEdges(fig) {
108 | var edges = [];
109 | var len = fig.length;
110 | for (var i = 0; i < len; i++) {
111 | edges.push([
112 | {x: fig[(i % len)].x, y: fig[(i % len)].y},
113 | {x: fig[((i+1) % len)].x, y: fig[((i+1) % len)].y}
114 | ]);
115 | }
116 | return edges;
117 | }
118 |
119 | function findEdgeIntersection(edge1, edge2) {
120 | var x1 = edge1[0].x;
121 | var x2 = edge1[1].x;
122 | var x3 = edge2[0].x;
123 | var x4 = edge2[1].x;
124 | var y1 = edge1[0].y;
125 | var y2 = edge1[1].y;
126 | var y3 = edge2[0].y;
127 | var y4 = edge2[1].y;
128 | var nom1 = +(((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)).toFixed(9));
129 | var nom2 = +(((x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3)).toFixed(9));
130 | var denom = +(((y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1)).toFixed(9));
131 | var t1 = nom1 / denom;
132 | var t2 = nom2 / denom;
133 | var interPoints = [];
134 | //1. lines are parallel or edges don't intersect
135 | if (((denom === 0) && (nom1 !== 0)) || (t1 <= 0) || (t1 >= 1) || (t2 < 0 ) || (t2 > 1)) {
136 | return interPoints;
137 | }
138 | //2. lines are collinear
139 | else if ((nom1 === 0) && (denom === 0)) {
140 | //check if endpoints of edge2 lies on edge1
141 | for (var i = 0; i < 2; i++) {
142 | var classify = classifyPoint(edge2[i], edge1);
143 | //find position of this endpoints relatively to edge1
144 | if (classify.loc == "ORIGIN" || classify.loc == "DESTINATION") {
145 | interPoints.push({x: edge2[i].x, y: edge2[i].y, t: classify.t});
146 | }
147 | else if (classify.loc == "BETWEEN") {
148 | x = +((x1 + classify.t*(x2 - x1)).toFixed(9));
149 | y = +((y1 + classify.t*(y2 - y1)).toFixed(9));
150 | interPoints.push({x: x, y: y, t: classify.t});
151 | }
152 | }
153 | return interPoints;
154 | }
155 | //3. edges intersect
156 | else {
157 | for (var i = 0; i < 2; i++) {
158 | var classify = classifyPoint(edge2[i], edge1);
159 | if (classify.loc == "ORIGIN" || classify.loc == "DESTINATION") {
160 | interPoints.push({x: edge2[i].x, y: edge2[i].y, t: classify.t});
161 | }
162 | }
163 | if (interPoints.length > 0) {
164 | return interPoints;
165 | }
166 | var x = +((x1 + t1*(x2 - x1)).toFixed(9));
167 | var y = +((y1 + t1*(y2 - y1)).toFixed(9));
168 | interPoints.push({x: x, y: y, t: t1});
169 | return interPoints;
170 | }
171 | }
172 |
173 | function classifyPoint(p, edge) {
174 | var ax = edge[1].x - edge[0].x;
175 | var ay = edge[1].y - edge[0].y;
176 | var bx = p.x - edge[0].x;
177 | var by = p.y - edge[0].y;
178 | var sa = ax * by - bx * ay;
179 | if ((p.x === edge[0].x) && (p.y === edge[0].y)) {
180 | return {loc: "ORIGIN", t: 0};
181 | }
182 | if ((p.x === edge[1].x) && (p.y === edge[1].y)) {
183 | return {loc: "DESTINATION", t: 1};
184 | }
185 | var theta = (polarAngle([edge[1], edge[0]]) -
186 | polarAngle([{x: edge[1].x, y: edge[1].y}, {x: p.x, y: p.y}])) % 360;
187 | if (theta < 0) {
188 | theta = theta + 360;
189 | }
190 | if (sa < -0.000000001) {
191 | return {loc: "LEFT", theta: theta};
192 | }
193 | if (sa > 0.000000001) {
194 | return {loc: "RIGHT", theta: theta};
195 | }
196 | if (((ax * bx) < 0) || ((ay * by) < 0)) {
197 | return {loc: "BEHIND", theta: theta};
198 | }
199 | if ((Math.sqrt(ax * ax + ay * ay)) < (Math.sqrt(bx * bx + by * by))) {
200 | return {loc: "BEYOND", theta: theta};
201 | }
202 | var t;
203 | if (ax !== 0) {
204 | t = bx/ax;
205 | } else {
206 | t = by/ay;
207 | }
208 | return {loc: "BETWEEN", t: t};
209 | }
210 |
211 | function polarAngle(edge) {
212 | var dx = edge[1].x - edge[0].x;
213 | var dy = edge[1].y - edge[0].y;
214 | if ((dx === 0) && (dy === 0)) {
215 | //console.error("Edge has zero length.");
216 | return false;
217 | }
218 | if (dx === 0) {
219 | return ((dy > 0) ? 90 : 270);
220 | }
221 | if (dy === 0) {
222 | return ((dx > 0) ? 0 : 180);
223 | }
224 | var theta = Math.atan(dy/dx)*360/(2*Math.PI);
225 | if (dx > 0) {
226 | return ((dy >= 0) ? theta : theta + 360);
227 | } else {
228 | return (theta + 180);
229 | }
230 | }
231 |
232 | function pointExists(p, points) {
233 | if (points.length === 0) {
234 | return false;
235 | }
236 | for (var i = 0; i < points.length; i++) {
237 | if ((p.x === points[i].x) && (p.y === points[i].y)) {
238 | return true;
239 | }
240 | }
241 | return false;
242 | }
243 |
244 | function edgeExists(e, edges) {
245 | if (edges.length === 0) {
246 | return false;
247 | }
248 | for (var i = 0; i < edges.length; i++) {
249 | if (equalEdges(e, edges[i]))
250 | return true;
251 | }
252 | return false;
253 | }
254 |
255 | function equalEdges(edge1, edge2) {
256 | if (((edge1[0].x === edge2[0].x) &&
257 | (edge1[0].y === edge2[0].y) &&
258 | (edge1[1].x === edge2[1].x) &&
259 | (edge1[1].y === edge2[1].y)) || (
260 | (edge1[0].x === edge2[1].x) &&
261 | (edge1[0].y === edge2[1].y) &&
262 | (edge1[1].x === edge2[0].x) &&
263 | (edge1[1].y === edge2[0].y))) {
264 | return true;
265 | } else {
266 | return false;
267 | }
268 | }
269 |
270 | function polygonate(edges) {
271 | var polygons = [];
272 | var polygon = [];
273 | var len = edges.length;
274 | var midpoints = getMidpoints(edges);
275 | //start from every edge and create non-selfintersecting polygons
276 | for (var i = 0; i < len - 2; i++) {
277 | var org = {x: edges[i][0].x, y: edges[i][0].y};
278 | var dest = {x: edges[i][1].x, y: edges[i][1].y};
279 | var currentEdge = i;
280 | var point;
281 | var p;
282 | var direction;
283 | var stop;
284 | //while we havn't come to the starting edge again
285 | for (direction = 0; direction < 2; direction++) {
286 | polygon = [];
287 | stop = false;
288 | while ((polygon.length === 0) || (!stop)) {
289 | //add point to polygon
290 | polygon.push({x: org.x, y: org.y});
291 | point = undefined;
292 | //look for edge connected with end of current edge
293 | for (var j = 0; j < len; j++) {
294 | p = undefined;
295 | //except itself
296 | if (!equalEdges(edges[j], edges[currentEdge])) {
297 | //if some edge is connected to current edge in one endpoint
298 | if ((edges[j][0].x === dest.x) && (edges[j][0].y === dest.y)) {
299 | p = edges[j][1];
300 | }
301 | if ((edges[j][1].x === dest.x) && (edges[j][1].y === dest.y)) {
302 | p = edges[j][0];
303 | }
304 | //compare it with last found connected edge for minimum angle between itself and current edge
305 | if (p) {
306 | var classify = classifyPoint(p, [org, dest]);
307 | //if this edge has smaller theta then last found edge update data of next edge of polygon
308 | if (!point ||
309 | ((classify.theta < point.theta) && (direction === 0)) ||
310 | ((classify.theta > point.theta) && (direction === 1))) {
311 | point = {x: p.x, y: p.y, theta: classify.theta, edge: j};
312 | }
313 | }
314 | }
315 | }
316 | //change current edge to next edge
317 | org.x = dest.x;
318 | org.y = dest.y;
319 | dest.x = point.x;
320 | dest.y = point.y;
321 | currentEdge = point.edge;
322 | //if we reach start edge
323 | if (equalEdges([org, dest], edges[i])) {
324 | stop = true;
325 | //check polygon for correctness
326 | /*for (var k = 0; k < allPoints.length; k++) {
327 | //if some point is inside polygon it is incorrect
328 | if ((!pointExists(allPoints[k], polygon)) && (findPointInsidePolygon(allPoints[k], polygon))) {
329 | polygon = false;
330 | }
331 | }*/
332 | for (k = 0; k < midpoints.length; k++) {
333 | //if some midpoint is inside polygon (edge inside polygon) it is incorrect
334 | if (findPointInsidePolygon(midpoints[k], polygon)) {
335 | polygon = false;
336 | }
337 | }
338 | }
339 | }
340 | //add created polygon if it is correct and was not found before
341 | if (polygon && !polygonExists(polygon, polygons)) {
342 | polygons.push(polygon);
343 | }
344 | }
345 | }
346 | //console.log("polygonate: " + JSON.stringify(polygons));
347 | return polygons;
348 | }
349 |
350 | function polygonExists(polygon, polygons) {
351 | //if array is empty element doesn't exist in it
352 | if (polygons.length === 0) return false;
353 | //check every polygon in array
354 | for (var i = 0; i < polygons.length; i++) {
355 | //if lengths are not same go to next element
356 | if (polygon.length !== polygons[i].length) continue;
357 | //if length are same need to check
358 | else {
359 | //if all the points are same
360 | for (var j = 0; j < polygon.length; j++) {
361 | //if point is not found break forloop and go to next element
362 | if (!pointExists(polygon[j], polygons[i])) break;
363 | //if point found
364 | else {
365 | //and it is last point in polygon we found polygon in array!
366 | if (j === polygon.length - 1) return true;
367 | }
368 | }
369 | }
370 | }
371 | return false;
372 | }
373 |
374 | function filterPolygons(polygons, fig1, fig2, mode) {
375 | var filtered = [];
376 | var c1, c2;
377 | var point;
378 | var bigPolygons = removeSmallPolygons(polygons, 0.0001);
379 | for(var i = 0; i < bigPolygons.length; i++) {
380 | point = getPointInsidePolygon(bigPolygons[i]);
381 | c1 = findPointInsidePolygon(point, fig1);
382 | c2 = findPointInsidePolygon(point, fig2);
383 | if (
384 | ((mode === "intersect") && c1 && c2) || //intersection
385 | ((mode === "cut1") && c1 && !c2) || //fig1 - fig2
386 | ((mode === "cut2") && !c1 && c2) || //fig2 - fig1
387 | ((mode === "sum") && (c1 || c2))) { //fig1 + fig2
388 | filtered.push(bigPolygons[i]);
389 | }
390 | }
391 | //console.log("filtered: " + JSON.stringify(filtered));
392 | return filtered;
393 | }
394 |
395 | function removeSmallPolygons(polygons, minSize) {
396 | var big = [];
397 | for (var i = 0; i < polygons.length; i++) {
398 | if (polygonArea(polygons[i]) >= minSize) {
399 | big.push(polygons[i]);
400 | }
401 | }
402 | return big;
403 | }
404 |
405 | function polygonArea(p) {
406 | var len = p.length;
407 | var s = 0;
408 | for (var i = 0; i < len; i++) {
409 | s += (p[i % len].x * p[(i + 1) % len].y) - (p[i % len].y *
410 | p[(i + 1) % len].x);
411 | }
412 | return Math.abs(s/2);
413 | }
414 |
415 | function getPointInsidePolygon(polygon) {
416 | var point;
417 | var size = getSize(polygon);
418 | var edges = getEdges(polygon);
419 | var y = size.y.min + (size.y.max - size.y.min) / Math.PI;
420 | var dy = (size.y.max - size.y.min) / 13;
421 | var line = [];
422 | var points;
423 | var interPoints = [];
424 | var pointsOK = false;
425 | while (!pointsOK) {
426 | line = [{x: (size.x.min - 1), y: y},{x: (size.x.max + 1), y: y}];
427 | //find intersections with all polygon edges
428 | for (var i = 0; i < edges.length; i++) {
429 | points = findEdgeIntersection(line, edges[i]);
430 | //if edge doesn't lie inside line
431 | if (points && (points.length === 1)) {
432 | interPoints.push(points[0]);
433 | }
434 | }
435 | interPoints = sortPoints(interPoints);
436 | //find two correct interpoints
437 | for (var i = 0; i < interPoints.length - 1; i++) {
438 | if (interPoints[i].t !== interPoints[i+1].t) {
439 | //enable exit from loop and calculate point coordinates
440 | pointsOK = true;
441 | point = {x: ((interPoints[i].x + interPoints[i+1].x) / 2), y: y};
442 | }
443 | }
444 | //all points are incorrect, need to change line parameters
445 | y = y + dy;
446 | if (((y > size.y.max) || (y < size.y.min)) && (pointsOK === false)) {
447 | pointsOK = true;
448 | point = undefined;
449 | }
450 | }
451 | return point;
452 | }
453 |
454 | function getSize(polygon) {
455 | var size = {
456 | x: {
457 | min: polygon[0].x,
458 | max: polygon[0].x
459 | },
460 | y: {
461 | min: polygon[0].y,
462 | max: polygon[0].y
463 | }
464 | };
465 | for (var i = 1; i < polygon.length; i++) {
466 | if (polygon[i].x < size.x.min) size.x.min = polygon[i].x;
467 | if (polygon[i].x > size.x.max) size.x.max = polygon[i].x;
468 | if (polygon[i].y < size.y.min) size.y.min = polygon[i].y;
469 | if (polygon[i].y > size.y.max) size.y.max = polygon[i].y;
470 | }
471 | return size;
472 | }
473 |
474 | function findPointInsidePolygon(point, polygon) {
475 | var cross = 0;
476 | var edges = getEdges(polygon);
477 | var classify;
478 | var org, dest;
479 | for (var i = 0; i < edges.length; i++) {
480 | [org, dest] = edges[i];
481 | classify = classifyPoint(point, [org, dest]);
482 | if ( (
483 | (classify.loc === "RIGHT") &&
484 | (org.y < point.y) &&
485 | (dest.y >= point.y)
486 | ) ||
487 | (
488 | (classify.loc === "LEFT") &&
489 | (org.y >= point.y) &&
490 | (dest.y < point.y)
491 | )
492 | ) {
493 | cross++;
494 | }
495 | if (classify.loc === "BETWEEN") return false;
496 | }
497 | if (cross % 2) {
498 | return true;
499 | } else {
500 | return false;
501 | }
502 | }
503 |
504 | function getMidpoints(edges) {
505 | var midpoints = [];
506 | var x, y;
507 | for (var i = 0; i < edges.length; i++) {
508 | x = (edges[i][0].x + edges[i][1].x) / 2;
509 | y = (edges[i][0].y + edges[i][1].y) / 2;
510 | classify = classifyPoint({x: x, y:y}, edges[i]);
511 | if (classify.loc != "BETWEEN") {
512 | console.error("Midpoint calculation error");
513 | }
514 | midpoints.push({x: x, y: y});
515 | }
516 | return midpoints;
517 | }
518 |
519 | function log(obj) {
520 | console.log(JSON.stringify(obj));
521 | }
522 |
523 |
524 | // fig1 = [
525 | // { x: 5.35328472172063, y: 3.3464605876540254 },
526 | // { x: 31.10025450900146, y: 3.3464605876540254 },
527 | // { x: 31.10025450900146, y: 38.65353941234598 },
528 | // { x: 5.35328472172063, y: 38.65353941234598 }
529 | // ];
530 | // fig2 = [
531 | // { x: 31.10025450900146, y: 6.964961212615723 },
532 | // { x: 5.35328472172063, y: 3.3464605876540254 },
533 | // { x: 26.64671527827937, y: 38.65353941234598 }
534 | // ];
535 |
536 | // default test
537 | // fig1 = [
538 | // { x: 100, y: 200 },
539 | // { x: 300, y: 150 },
540 | // { x: 300, y: 250 }
541 | // ];
542 |
543 | // fig2 = [
544 | // { x: 200, y: 100 },
545 | // { x: 200, y: 300 },
546 | // { x: 350, y: 300 },
547 | // { x: 350, y: 100 }
548 | // ];
549 |
550 | // console.log("DEBUG STARTED");
551 | // var result = intersect(fig1, fig2);
552 | // log(result);
553 | // console.log("DEBUG STOPPED");
554 |
--------------------------------------------------------------------------------
/test.css:
--------------------------------------------------------------------------------
1 | html, body {
2 | width: 100%;
3 | height: 100%;
4 | }
5 |
6 | .container {
7 | border: solid lightgray 3px;
8 | width: 500px;
9 | height: 500px;
10 | position: absolute;
11 | right: 5%;
12 | top: 5%;
13 |
14 | }
15 |
16 | .container svg {
17 | position: absolute;
18 | left: 0;
19 | top: 0;
20 | width: 100%;
21 | height: 100%;
22 | }
23 |
24 | svg.base polygon {
25 | stroke: black;
26 | stroke-width: 2px;
27 | opacity: 0.5;
28 | fill-rule:evenodd;
29 | }
30 |
31 | @keyframes blink {
32 | 0% { opacity: 0; }
33 | 50% { opacity: 1; }
34 | 100% { opacity: 0; }
35 | }
36 |
37 | svg.intersections {
38 | animation-name: blink;
39 | animation-duration: 1s;
40 | animation-iteration-count: infinite;
41 | }
42 |
--------------------------------------------------------------------------------
/test.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | Tests | Intersections in JS
13 |
14 |
15 |
16 |
17 |
20 |
21 |
22 |
23 |
24 |
25 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
56 |
57 |
58 |
--------------------------------------------------------------------------------
/test.js:
--------------------------------------------------------------------------------
1 | //default test
2 | fig1 = [
3 | { x: 100, y: 200 },
4 | { x: 300, y: 150 },
5 | { x: 300, y: 250 }
6 | ];
7 |
8 | fig2 = [
9 | { x: 200, y: 100 },
10 | { x: 200, y: 300 },
11 | { x: 350, y: 300 },
12 | { x: 350, y: 100 }
13 | ];
14 |
15 | //test #1 from @HatScripts
16 | // fig1 = [
17 | // { x: 5.35328472172063, y: 3.3464605876540254 },
18 | // { x: 31.10025450900146, y: 3.3464605876540254 },
19 | // { x: 31.10025450900146, y: 38.65353941234598 },
20 | // { x: 5.35328472172063, y: 38.65353941234598 }
21 | // ];
22 | // fig2 = [
23 | // { x: 31.10025450900146, y: 6.964961212615723 },
24 | // { x: 5.35328472172063, y: 3.3464605876540254 },
25 | // { x: 26.64671527827937, y: 38.65353941234598 }
26 | // ];
27 |
28 | //test #2 from @HatScripts
29 | // fig1 = [
30 | // { x: 10.143593539448977, y: 18.143593539448986 },
31 | // { x: 37.856406460551014, y: 18.143593539448986 },
32 | // { x: 37.856406460551014, y: 45.856406460551014 },
33 | // { x: 10.143593539448977, y: 45.856406460551014 }
34 | // ];
35 | // fig2 = [
36 | // { x: 37.856406460551014, y: 18.14359353944898 },
37 | // { x: 10.143593539448977, y: 2.143593539448986 },
38 | // { x: 21.85640646055102, y: 45.856406460551014 }
39 | // ];
40 |
41 | //test #1 from @Skeptron
42 | // var fig1 = [
43 | // {x: 1000, y: 800},
44 | // {x: 800, y: 500},
45 | // {x: 600, y: 500},
46 | // {x: 474, y: 800},
47 | // {x: 1000, y: 800}
48 | // ];
49 | // var fig2 = [
50 | // {x: 1000, y: 400},
51 | // {x: 400, y: 200},
52 | // {x: 200, y: 200},
53 | // {x: 200, y: 300},
54 | // {x: 449, y: 800},
55 | // {x: 1000, y: 800}
56 | // ];
57 |
58 | //test #2 from @Skeptron
59 | // fig1 = [
60 | // {x: 926, y: 0},
61 | // {x: 600, y: 500},
62 | // {x: 600, y: 600},
63 | // {x: 799, y: 600},
64 | // {x: 1000, y: 559},
65 | // {x: 1000, y: 0}
66 | // ];
67 | // fig2 = [
68 | // {x: 1000, y: 400},
69 | // {x: 400, y: 200},
70 | // {x: 200, y: 200},
71 | // {x: 200, y: 300},
72 | // {x: 449, y: 800},
73 | // {x: 1000, y: 800}
74 | // ];
75 |
76 | //test from @clemosm
77 | // fig1 = [
78 | // {x:383.11684890961,y:822.2939549877},
79 | // {x:383.08019699532,y:824.4200341334},
80 | // {x:381.523363302535,y:824.3935903418},
81 | // {x:381.46925809573,y:827.5166057295},
82 | // {x:379.9211510492,y:827.4901618769},
83 | // {x:378.91933205856,y:827.4742955655},
84 | // {x:379.01183450892,y:822.2278456159},
85 | // {x:380.21087570504,y:822.2463562398},
86 | // {x:380.18120510776,y:823.9942891567},
87 | // {x:380.844430223515,y:824.0075110482},
88 | // {x:380.874100820794,y:822.2542893642}
89 | // ];
90 | // fig2 = [
91 | // {x:383.08019699532,y:824.4200341334},
92 | // {x:381.523363302535,y:824.3935903418},
93 | // {x:380.823486272486,y:824.3909459626},
94 | // {x:380.844430223515,y:824.0075110482},
95 | // {x:380.874100820794,y:822.2542893642},
96 | // {x:383.11684890961,y:822.2939549877}
97 | // ];
98 |
99 | //test from @prendradjaja
100 | // fig1 = [
101 | // {"x":1,"y":0},
102 | // {"x":1.0000000000000002,"y":1},
103 | // {"x":0.29289321881345287,"y":1.7071067811865477},
104 | // {"x":0.2928932188134526,"y":0.7071067811865477}
105 | // ];
106 | // fig2 = [
107 | // {"x":0.29289321881345254,"y":0.7071067811865476},
108 | // {"x":1.0000000000000002,"y":1.414213562373095},
109 | // {"x":2.220446049250313e-16,"y":1.4142135623730951},
110 | // {"x":-0.7071067811865476,"y":0.7071067811865479}
111 | // ];
112 |
113 | //second test from @prendradjaja
114 | // fig1 = [
115 | // {"x":99.99999999999999,"y":0},
116 | // {"x":200,"y":-1.2246467991473532e-14},
117 | // {"x":200,"y":99.99999999999999},
118 | // {"x":100,"y":100.00000000000003}
119 | // ];
120 | // fig2 = [
121 | // {"x":0,"y":0},
122 | // {"x":200,"y":0},
123 | // {"x":270.71067811865476,"y":70.71067811865476},
124 | // {"x":270.71067811865476,"y":270.71067811865476},
125 | // {"x":200,"y":341.4213562373095},
126 | // {"x":0,"y":341.4213562373095},
127 | // {"x":-70.71067811865476,"y":270.71067811865476},
128 | // {"x":-70.71067811865476,"y":70.71067811865474}
129 | // ];
130 |
131 | // Test for issue #8 from @northernstream
132 | // fig1=[
133 | // { x: 10, y: 0 },
134 | // { x: -10, y: 1.2246467991473533e-15 },
135 | // { x: -10, y: 1.2246467991473533e-15 },
136 | // { x: -9.92546151641322, y: 1.2186934340514755 },
137 | // { x: -9.702957262759965, y: 2.4192189559966772 },
138 | // { x: -9.335804264972015, y: 3.5836794954530067 },
139 | // { x: -8.82947592858927, y: 4.694715627858907 },
140 | // { x: -8.191520442889919, y: 5.73576436351046 },
141 | // { x: -7.431448254773944, y: 6.691306063588581 },
142 | // { x: -6.560590289905072, y: 7.547095802227721 },
143 | // { x: -5.591929034707467, y: 8.290375725550417 },
144 | // { x: -4.5399049973954675, y: 8.910065241883679 },
145 | // { x: -3.420201433256687, y: 9.396926207859085 },
146 | // { x: -2.249510543438648, y: 9.743700647852352 },
147 | // { x: -1.0452846326765333, y: 9.945218953682733 },
148 | // { x: 0.174524064372836, y: 9.998476951563912 },
149 | // { x: 1.3917310096006545, y: 9.902680687415703 },
150 | // { x: 2.5881904510252074, y: 9.659258262890683 },
151 | // { x: 3.746065934159122, y: 9.271838545667872 },
152 | // { x: 4.848096202463371, y: 8.746197071393958 },
153 | // { x: 5.877852522924732, y: 8.090169943749475 },
154 | // { x: 6.819983600624985, y: 7.313537016191704 },
155 | // { x: 7.66044443118978, y: 6.4278760968653925 },
156 | // { x: 8.386705679454241, y: 5.44639035015027 },
157 | // { x: 8.98794046299167, y: 4.383711467890774 },
158 | // { x: 9.455185755993169, y: 3.255681544571567 },
159 | // { x: 9.781476007338057, y: 2.079116908177593 },
160 | // { x: 9.961946980917455, y: 0.8715574274765816 },
161 | // { x: 10, y: 0 }
162 | // ];
163 | // fig2=[
164 | // { x: -3.4641016151399997, y: 3.6399999999999997 },
165 | // { x: -2.7767585183349683, y: 3.5979602952570557 },
166 | // { x: -2.099662123957873, y: 3.47246789619662 },
167 | // { x: -1.4429063797045063, y: 3.265393605444218 },
168 | // { x: -0.816282001027576, y: 2.979824423724347 },
169 | // { x: -0.22913051412010077, y: 2.620017529789913 },
170 | // { x: 0.30979500472396015, y: 2.1913368156925035 },
171 | // { x: 0.7924604173164336, y: 1.70017292350646 },
172 | // { x: 1.211670294070434, y: 1.1538479755750122 },
173 | // { x: 1.5611751812823944, y: 0.5605064185310433 },
174 | // { x: 1.8357647660925234, y: -0.07100639164322864 },
175 | // { x: 2.031345550248726, y: -0.7312760535006015 },
176 | // { x: 2.1450018747370607, y: -1.4104594671704347 },
177 | // { x: 2.1750393855420467, y: -2.0984315723062843 },
178 | // { x: 2.1210102925624557, y: -2.7849362894147713 },
179 | // { x: 1.9837200451303452, y: -3.4597394143782165 },
180 | // { x: 1.7652153246166802, y: -4.112781186865746 },
181 | // { x: 1.468753533126192, y: -4.7343262581893395 },
182 | // { x: 1.0987542331347027, y: -5.315108822929549 },
183 | // { x: 0.6607332619921222, y: -5.84647075075249 },
184 | // { x: 0.16122050349208106, y: -6.320490659191036 },
185 | // { x: -0.3923374576552501, y: -6.730102003212193 },
186 | // { x: -0.9916883472496036, y: -7.069198421127302 },
187 | // { x: -1.6278972240016387, y: -7.332724766380148 },
188 | // { x: -2.2914796789278418, y: -7.516752468138664 },
189 | // { x: -2.97254322604321, y: -7.618538097237445 },
190 | // { x: -3.6609347765421094, y: -7.636564264387699 },
191 | // { x: -4.3463919979669035, y: -7.570562240956576 },
192 | // { x: -5.018696301947874, y: -7.4215159650921185 },
193 | // { x: -5.667825179819504, y: -7.1916473734717625 },
194 | // { x: -6.284101615140003, y: -6.884383277344233 },
195 | // { x: -6.858338345717552, y: -6.504304276666732 },
196 | // { x: -7.381974824528745, y: -6.057076473909991 },
197 | // { x: -7.847204837757316, y: -5.549367005521084 },
198 | // { x: -8.247092877462244, y: -4.988744650275275 },
199 | // { x: -8.575677534026706, y: -4.383566996217544 },
200 | // { x: -8.828060367044666, y: -3.742855848274705 },
201 | // { x: -9.000478929784824, y: -3.076162733923712 },
202 | // { x: -9.09036285860541, y: -2.393426511916864 },
203 | // { x: -9.096372191155796, y: -1.7048252067897969 },
204 | // { x: -9.018417342128853, y: -1.0206242779585133 },
205 | // { x: -8.85766043877152, y: -0.3510235853637631 },
206 | // { x: -8.616497996244268, y: 0.2939946669475142 },
207 | // { x: -8.298525191099912, y: 0.9048147424927064 },
208 | // { x: -7.908482265481911, y: 1.4723307208367133 },
209 | // { x: -7.452183861032127, y: 1.988082245892128 },
210 | // { x: -6.936432335976713, y: 2.4443806503419117 },
211 | // { x: -6.368916357632706, y: 2.8344235759599137 },
212 | // { x: -5.758096282087513, y: 3.1523963811042695 },
213 | // { x: -5.113078029776235, y: 3.39355882363152 },
214 | // { x: -4.443477337181487, y: 3.554315726988853 },
215 | // { x: -3.7592764083502024, y: 3.632270576015796 },
216 | // { x: -3.4641016151399997, y: 3.6399999999999997 }
217 | // ];
218 |
219 | // Test for issue #12 from @ptarabbia
220 | // fig1 = [
221 | // {"x": 555.185880441, "y": 1160.301884319},
222 | // {"x": 545.182378187, "y": 934.767230421},
223 | // {"x": 817.285926289, "y": 966.451869566}
224 | // ];
225 |
226 | // fig2 = [
227 | // {"x": 965.05163647, "y": 834.497760783},
228 | // {"x": 838.848023757, "y": 968.962631672},
229 | // {"x": 545.182378187, "y": 934.767230421}
230 | // ];
231 |
232 |
233 | edges = [
234 | [
235 | { x: 100, y: 200 },
236 | { x: 300, y: 150 }
237 | ],
238 | [
239 | { x: 300, y: 150 },
240 | { x: 300, y: 250 }
241 | ]
242 | ];
243 |
244 | polygons = [
245 | [
246 | {x: 100, y: 200},
247 | {x: 200, y: 175},
248 | {x: 200, y: 225}
249 | ],
250 | [
251 | {x: 200, y: 175},
252 | {x: 300, y: 150},
253 | {x: 300, y: 250},
254 | {x: 200, y: 225}
255 | ],
256 | [
257 | {x: 200, y: 175},
258 | {x: 300, y: 150},
259 | {x: 300, y: 250},
260 | {x: 200, y: 225},
261 | {x: 200, y: 300},
262 | {x: 350, y: 300},
263 | {x: 350, y: 100},
264 | {x: 200, y: 100}
265 | ]
266 | ];
267 |
268 | small = [
269 | {x: 0, y: 0},
270 | {x: 1, y: 0},
271 | {x: 1, y: 1}
272 | ];
273 |
274 | mesh = [
275 | [
276 | {x: 100, y: 200},
277 | {x: 200, y: 175}
278 | ],
279 | [
280 | {x: 200, y: 175},
281 | {x: 300, y: 150}
282 | ],
283 | [
284 | {x: 300, y: 150},
285 | {x: 300, y: 250}
286 | ],
287 | [
288 | {x: 300, y: 250},
289 | {x: 200, y: 225}
290 | ],
291 | [
292 | {x: 200, y: 225},
293 | {x: 100, y: 200}
294 | ],
295 | [
296 | {x: 200, y: 100},
297 | {x: 200, y: 175}
298 | ],
299 | [
300 | {x: 200, y: 175},
301 | {x: 200, y: 225}
302 | ],
303 | [
304 | {x: 200, y: 225},
305 | {x: 200, y: 300}
306 | ],
307 | [
308 | {x: 200, y: 300},
309 | {x: 350, y: 300}
310 | ],
311 | [
312 | {x: 350, y: 300},
313 | {x: 350, y: 100}
314 | ],
315 | [
316 | {x: 350, y: 100},
317 | {x: 200, y: 100}
318 | ]
319 | ];
320 |
321 |
322 | describe("intersect", function() {
323 | it("returns polygons intersection", function() {
324 | assert.deepEqual(intersect(fig1, fig2), [polygons[1]]);
325 | });
326 | });
327 |
328 | describe("edgify", function() {
329 | it("returns edges and points from polygons", function() {
330 | assert.deepEqual(edgify(fig1, fig2), mesh);
331 | });
332 | });
333 |
334 | describe("polygonate", function() {
335 | it("returns nonintersecting polygons from edges", function() {
336 | assert.deepEqual(polygonate(mesh), polygons);
337 | });
338 | });
339 |
340 | describe("filterPolygons", function() {
341 | it("returns polygons that satisfy condition: intersect", function() {
342 | assert.deepEqual(filterPolygons(polygons, fig1, fig2, "intersect"), [polygons[1]]);
343 | });
344 | it("returns polygons that satisfy condition: cut1", function() {
345 | assert.deepEqual(filterPolygons(polygons, fig1, fig2, "cut1"), [polygons[0]]);
346 | });
347 | it("returns polygons that satisfy condition: cut2", function() {
348 | assert.deepEqual(filterPolygons(polygons, fig1, fig2, "cut2"), [polygons[2]]);
349 | });
350 | it("returns polygons that satisfy condition: sum", function() {
351 | assert.deepEqual(filterPolygons(polygons, fig1, fig2, "sum"), polygons);
352 | });
353 | });
354 |
355 | describe("polygonArea", function() {
356 | it("returnes polygon area", function() {
357 | assert.deepEqual(polygonArea(small), 0.5);
358 | });
359 | });
360 |
361 | describe("removeSmallPolygons", function() {
362 | it("returnes array without small polygons", function() {
363 | assert.deepEqual(removeSmallPolygons(polygons.concat(small), 0.00000001), polygons);
364 | });
365 | });
366 |
367 | describe("sortPoints", function() {
368 | it("returns array of points sorted by t value", function() {
369 | assert.deepEqual(sortPoints([{x: 1, y: 2, t: 3}, {x: 4, y: 5, t: 1}, {x: 6, y: 7, t: -1}]),
370 | [{x: 6, y: 7, t: -1}, {x: 4, y: 5, t: 1}, {x: 1, y: 2, t: 3}]);
371 | });
372 | });
373 |
374 | describe("findPointInsidePolygon", function() {
375 | it("returns true if point is inside polygon", function() {
376 | assert.deepEqual(findPointInsidePolygon({x: 200, y: 200}, fig1), true);
377 | });
378 | it("returns false if point is outside polygon", function() {
379 | assert.deepEqual(findPointInsidePolygon({x: 50, y: 200}, fig1), false);
380 | });
381 | });
382 |
383 | describe("getSize", function() {
384 | it("returns size of polygon", function() {
385 | assert.deepEqual(getSize(fig1), {x: {min: 100, max: 300}, y: {min: 150, max: 250}});
386 | });
387 | });
388 |
389 | // describe("getPointInsidePolygon", function() {
390 |
391 | // it("returns point located inside polygon 1", function() {
392 | // assert.deepEqual(getPointInsidePolygon(fig1), {x: 200, y: 200});
393 | // });
394 | // it("returns point located inside polygon 2", function() {
395 | // assert.deepEqual(getPointInsidePolygon(fig2), {x: 275, y: 200});
396 | // });
397 | // });
398 |
399 | describe("checkPolygons", function() {
400 | it("returns true if polygons are valid", function() {
401 | assert.equal(checkPolygons(fig1, fig2), true);
402 | });
403 | it("returns false if first polygon is invalid", function() {
404 | assert.equal(checkPolygons(fig1.slice(0, 2), fig2), false);
405 | });
406 | it("returns false if second polygon is invalid", function() {
407 | assert.equal(checkPolygons(fig1, fig2.slice(0, 2)), false);
408 | });
409 | });
410 |
411 | describe("getEdges", function() {
412 | it("returns edges from polygon specified with points", function() {
413 | assert.deepEqual(getEdges(fig1), [
414 | [fig1[0], fig1[1]],
415 | [fig1[1], fig1[2]],
416 | [fig1[2], fig1[0]]
417 | ]);
418 | });
419 | });
420 |
421 | describe("pointExists", function() {
422 | it("returns true if point exists in array", function() {
423 | assert.deepEqual(pointExists(fig1[0], fig1), true);
424 | });
425 | it("returns false if point doesn't exist in array", function() {
426 | assert.deepEqual(pointExists(fig2[0], fig1), false);
427 | });
428 | });
429 |
430 | describe("edgeExists", function() {
431 | it("returns true if edge exists in array", function() {
432 | assert.deepEqual(edgeExists(edges[0], edges), true);
433 | });
434 | it("returns false if edge doesn't exist in array", function() {
435 | assert.deepEqual(edgeExists([{x: 110, y: 220}, {x: 280, y: 180}], edges), false);
436 | });
437 | });
438 |
439 | describe("equalEdges", function() {
440 | it("returns true if edges have same points", function() {
441 | assert.deepEqual(equalEdges(edges[0], edges[0]), true);
442 | });
443 | it("returns false if edges don't have same points", function() {
444 | assert.deepEqual(equalEdges(edges[0], edges[1]), false);
445 | });
446 | });
447 |
448 | describe("findEdgeIntersection", function() {
449 | it("returns false if edges are parallel", function() {
450 | assert.equal(findEdgeIntersection([{x: 3, y: 3}, {x: 5, y: 5}], [{x: 3, y: 4}, {x: 5, y: 6}]), false);
451 | });
452 | it("returns false if edges don't intersect", function() {
453 | assert.equal(findEdgeIntersection([{x: 3, y: 3}, {x: 5, y: 5}], [{x: 1, y: 7}, {x: 3, y: 5}]), false);
454 | });
455 | it("returns false if edges are collinear and don't intersect", function() {
456 | assert.equal(findEdgeIntersection([{x: 3, y: 3}, {x: 5, y: 5}], [{x: 6, y: 6}, {x: 8, y: 8}]), false);
457 | });
458 | it("returns point if edges are collinear and intersect at one endpoint", function() {
459 | assert.deepEqual(findEdgeIntersection([{x: 3, y: 3}, {x: 5, y: 5}], [{x: 4, y: 4}, {x: 6, y: 6}]),
460 | [{x: 4, y: 4, t: 0.5}]);
461 | });
462 | it("returns two points if edges are collinear and intersect at two endpoints", function() {
463 | assert.deepEqual(findEdgeIntersection([{x: 3, y: 3}, {x: 7, y: 7}], [{x: 4, y: 4}, {x: 6, y: 6}]),
464 | [{x: 4, y: 4, t: 0.25}, {x: 6, y: 6, t: 0.75}]);
465 | });
466 | it("returns point if edges intersect", function() {
467 | assert.deepEqual(findEdgeIntersection([{x: 3, y: 3}, {x: 5, y: 5}], [{x: 3, y: 5}, {x: 5, y: 3}]),
468 | [{x: 4, y: 4, t: 0.5}]);
469 | });
470 | });
471 |
472 | describe("classifyPoint", function() {
473 | it("point is at right", function() {
474 | assert.deepEqual(classifyPoint({x: 5, y: 3}, [{x: 3, y: 3}, {x: 5, y: 5}]), {loc: "LEFT", theta: 315});
475 | });
476 | it("point is at left", function() {
477 | assert.deepEqual(classifyPoint({x: 3, y: 5}, [{x: 3, y: 3}, {x: 5, y: 5}]), {loc: "RIGHT", theta: 45});
478 | });
479 | it("point is beyond", function() {
480 | assert.deepEqual(classifyPoint({x: 6, y: 6}, [{x: 3, y: 3}, {x: 5, y: 5}]), {loc: "BEYOND", theta: 180});
481 | });
482 | it("point is between", function() {
483 | assert.deepEqual(classifyPoint({x: 4, y: 4}, [{x: 3, y: 3}, {x: 5, y: 5}]), {loc: "BETWEEN", t: 0.5});
484 | });
485 | });
486 |
487 | describe("getMidpoints", function() {
488 | it("returns midpoints of edges", function() {
489 | assert.deepEqual(getMidpoints([[{x: 3, y: 3}, {x: 5, y: 5}], [{x: -1, y: 2}, {x: 0, y: 4}]]),
490 | [{x: 4, y: 4}, {x: -0.5, y: 3}]);
491 | });
492 | });
493 |
494 | describe("polarAngle", function() {
495 |
496 | it("returns false if edge has zero length", function() {
497 | assert.equal(polarAngle([{x: 3, y: 3}, {x: 3, y: 3}]), false);
498 | });
499 |
500 | it("returns 45 if angle is 45", function() {
501 | assert.equal(polarAngle([{x: 3, y: 3}, {x: 5, y: 5}]), 45);
502 | });
503 |
504 | it("returns 315 if angle is 315", function() {
505 | assert.equal(polarAngle([{x: 0, y: 0}, {x: 5, y: -5}]), 315);
506 | });
507 |
508 | });
509 |
510 | describe("polygonExists", function() {
511 | it("returns false if polygons array is empty", function() {
512 | assert.equal(polygonExists([fig2[0], fig1[1], fig1[2]], []), false);
513 | });
514 | it("returns false if polygon doesn't exist in array", function() {
515 | assert.equal(polygonExists([fig2[0], fig1[1], fig1[2]], [fig1, fig2]), false);
516 | });
517 | it("returns true if polygon existsin array", function() {
518 | assert.equal(polygonExists([fig1[0], fig1[1], fig1[2]], [fig1, fig2]), true);
519 | });
520 | });
--------------------------------------------------------------------------------