qSVG.js 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914
  1. //----------------------- Quick SVG LIBRARY --------------------------------------------------
  2. //----------------------- V1.0 Licence MIT ---------------------------------------------------
  3. //----------------------- Author : Patrick RASPINO--------------------------------------------
  4. //----------------------- 11/08/16 -----------------------------------------------------------
  5. // 'use strict';
  6. var qSVG = {
  7. create: function(id, shape, attrs) {
  8. var shape = $(document.createElementNS("http://www.w3.org/2000/svg", shape));
  9. for (var k in attrs) {
  10. shape.attr(k, attrs[k]);
  11. }
  12. if (id != 'none') {
  13. $("#" + id).append(shape);
  14. }
  15. return shape;
  16. },
  17. angleDeg: function(cx, cy, ex, ey) {
  18. var dy = ey - cy;
  19. var dx = ex - cx;
  20. var theta = Math.atan2(dy, dx); // range (-PI, PI]
  21. theta *= 180 / Math.PI; // rads to degs, range (-180, 180]
  22. if (theta < 0) theta = 360 + theta; // range [0, 360)
  23. return theta;
  24. },
  25. angle: function(x1, y1, x2, y2, x3, y3) {
  26. var x1 = parseInt(x1);
  27. var y1 = parseInt(y1);
  28. var x2 = parseInt(x2);
  29. var y2 = parseInt(y2);
  30. var anglerad;
  31. if (!x3) {
  32. if (x1 - x2 == 0) anglerad = Math.PI / 2;
  33. else {
  34. anglerad = Math.atan((y1 - y2) / (x1 - x2));
  35. }
  36. var angledeg = anglerad * 180 / Math.PI;
  37. } else {
  38. var x3 = parseInt(x3);
  39. var y3 = parseInt(y3);
  40. var a = Math.sqrt(Math.pow(Math.abs(x2 - x1), 2) + Math.pow(Math.abs(y2 - y1), 2));
  41. var b = Math.sqrt(Math.pow(Math.abs(x2 - x3), 2) + Math.pow(Math.abs(y2 - y3), 2));
  42. var c = Math.sqrt(Math.pow(Math.abs(x3 - x1), 2) + Math.pow(Math.abs(y3 - y1), 2));
  43. if (a == 0 || b == 0) anglerad = Math.PI / 2;
  44. else {
  45. anglerad = Math.acos((Math.pow(a, 2) + Math.pow(b, 2) - Math.pow(c, 2)) / (2 * a * b));
  46. }
  47. angledeg = (360 * anglerad) / (2*Math.PI);
  48. }
  49. return ({
  50. rad: anglerad,
  51. deg: angledeg
  52. });
  53. },
  54. getAngle: function(el1, el2) {
  55. return ({
  56. rad: Math.atan2(el2.y - el1.y, el2.x - el1.x),
  57. deg: Math.atan2(el2.y - el1.y, el2.x - el1.x)* 180 / Math.PI
  58. });
  59. },
  60. middle: function(xo, yo, xd, yd) {
  61. var x1 = parseInt(xo);
  62. var y1 = parseInt(yo);
  63. var x2 = parseInt(xd);
  64. var y2 = parseInt(yd);
  65. var middleX = Math.abs(x1 + x2) / 2;
  66. var middleY = Math.abs(y1 + y2) / 2;
  67. return ({
  68. x: middleX,
  69. y: middleY
  70. });
  71. },
  72. triangleArea: function(fp, sp, tp) {
  73. var A = 0;
  74. var B = 0;
  75. var C = 0;
  76. var p = 0;
  77. A = qSVG.measure(fp, sp);
  78. B = qSVG.measure(sp, tp);
  79. C = qSVG.measure(tp, fp);
  80. p = (A + B + C) / 2;
  81. return (Math.sqrt(p*(p-A)*(p-B)*(p-C)));
  82. },
  83. measure: function(po, pt) {
  84. return Math.sqrt(Math.pow(po.x - pt.x, 2) + Math.pow(po.y - pt.y, 2));
  85. },
  86. gap: function(po, pt) {
  87. return Math.pow(po.x - pt.x, 2) + Math.pow(po.y - pt.y, 2);
  88. },
  89. pDistance(point, pointA, pointB) {
  90. var x = point.x;
  91. var y = point.y;
  92. var x1 = pointA.x;
  93. var y1 = pointA.y;
  94. var x2 = pointB.x;
  95. var y2 = pointB.y;
  96. var A = x - x1;
  97. var B = y - y1;
  98. var C = x2 - x1;
  99. var D = y2 - y1;
  100. var dot = A * C + B * D;
  101. var len_sq = C * C + D * D;
  102. var param = -1;
  103. if (len_sq != 0) //in case of 0 length line
  104. param = dot / len_sq;
  105. var xx, yy;
  106. if (param < 0) {
  107. xx = x1;
  108. yy = y1;
  109. }
  110. else if (param > 1) {
  111. xx = x2;
  112. yy = y2;
  113. }
  114. else {
  115. xx = x1 + param * C;
  116. yy = y1 + param * D;
  117. }
  118. var dx = x - xx;
  119. var dy = y - yy;
  120. return ({
  121. x: xx,
  122. y: yy,
  123. distance: Math.sqrt(dx * dx + dy * dy)
  124. });
  125. },
  126. nearPointOnEquation: function(equation, point) { // Y = Ax + B ---- equation {A:val, B:val}
  127. var pointA = {};
  128. var pointB = {};
  129. if (equation.A == 'h') {
  130. return ({
  131. x: point.x,
  132. y: equation.B,
  133. distance: Math.abs(equation.B - point.y)
  134. });
  135. }
  136. else if (equation.A == 'v') {
  137. return ({
  138. x: equation.B,
  139. y: point.y,
  140. distance: Math.abs(equation.B - point.x)
  141. });
  142. }
  143. else {
  144. pointA.x = point.x;
  145. pointA.y = (equation.A * point.x) + equation.B;
  146. pointB.x = (point.y - equation.B)/equation.A;
  147. pointB.y = point.y;
  148. return qSVG.pDistance(point, pointA, pointB);
  149. }
  150. },
  151. circlePath: function(cx, cy, r){
  152. return 'M '+cx+' '+cy+' m -'+r+', 0 a '+r+','+r+' 0 1,0 '+(r*2)+',0 a '+r+','+r+' 0 1,0 -'+(r*2)+',0';
  153. },
  154. createEquation: function(x0, y0, x1, y1) {
  155. if (x1 - x0 == 0) {
  156. return ({
  157. A: 'v',
  158. B: x0
  159. });}
  160. else if (y1 - y0 == 0) {
  161. return ({
  162. A: 'h',
  163. B: y0
  164. });}
  165. else {
  166. return ({
  167. A: (y1 - y0) / (x1 - x0),
  168. B: y1 - (x1 * ((y1 - y0) / (x1 - x0)))
  169. });}
  170. },
  171. perpendicularEquation: function(equation, x1, y1) {
  172. if (typeof(equation.A) != "string") {
  173. return ({
  174. A: (-1 / equation.A),
  175. B: y1 - ((-1 / equation.A) * x1)
  176. });}
  177. if (equation.A == 'h') {
  178. return ({
  179. A: 'v',
  180. B: x1
  181. });}
  182. if (equation.A == 'v') {
  183. return ({
  184. A: 'h',
  185. B: y1
  186. });}
  187. },
  188. angleBetweenEquations: function(m1, m2) {
  189. if (m1 == 'h') m1 = 0;
  190. if (m2 == 'h') m2 = 0;
  191. if (m1 == 'v') m1 = 10000;
  192. if (m2 == 'v') m2 = 10000;
  193. var angleRad = Math.atan(Math.abs((m2 - m1) / (1 + (m1 * m2))));
  194. return (360 * angleRad) / (2*Math.PI);
  195. },
  196. // type array return [x,y] ---- type object return {x:x, y:y}
  197. intersectionOfEquations: function(equation1, equation2, type = "array", message = false) {
  198. var retArray;
  199. var retObj;
  200. if (equation1.A == equation2.A) {
  201. retArray = false;
  202. retObj = false;
  203. }
  204. if (equation1.A == 'v' && equation2.A == 'h') {
  205. retArray = [equation1.B, equation2.B];
  206. retObj = {x: equation1.B, y: equation2.B};
  207. }
  208. if (equation1.A == 'h' && equation2.A == 'v') {
  209. retArray = [equation2.B, equation1.B];
  210. retObj = {x: equation2.B, y: equation1.B};
  211. }
  212. if (equation1.A == 'h' && equation2.A != 'v' && equation2.A != 'h') {
  213. retArray = [(equation1.B - equation2.B)/equation2.A, equation1.B];
  214. retObj = {x: (equation1.B - equation2.B)/equation2.A, y: equation1.B};
  215. }
  216. if (equation1.A == 'v' && equation2.A != 'v' && equation2.A != 'h') {
  217. retArray = [equation1.B, (equation2.A * equation1.B) + equation2.B];
  218. retObj = {x: equation1.B, y: (equation2.A * equation1.B) + equation2.B};
  219. }
  220. if (equation2.A == 'h' && equation1.A != 'v' && equation1.A != 'h') {
  221. retArray = [(equation2.B - equation1.B)/equation1.A, equation2.B];
  222. retObj = {x: (equation2.B - equation1.B)/equation1.A, y: equation2.B};
  223. }
  224. if (equation2.A == 'v' && equation1.A != 'v' && equation1.A != 'h') {
  225. retArray = [equation2.B, (equation1.A * equation2.B) + equation1.B];
  226. retObj = {x: equation2.B, y: (equation1.A * equation2.B) + equation1.B};
  227. }
  228. if (equation1.A != 'h' && equation1.A != 'v' && equation2.A != 'v' && equation2.A != 'h') {
  229. var xT = (equation2.B - equation1.B) / (equation1.A - equation2.A);
  230. var yT = (equation1.A * xT) + equation1.B;
  231. retArray = [xT, yT];
  232. retObj = {x: xT, y: yT};
  233. }
  234. if (type == "array") return retArray;
  235. else return retObj;
  236. },
  237. vectorXY: function(obj1, obj2) {
  238. return ({
  239. x: obj2.x - obj1.x,
  240. y: obj2.y - obj1.y
  241. });
  242. },
  243. vectorAngle: function(v1, v2) {
  244. return (Math.atan2((v2.y-v1.y),(v2.x-v1.x))+Math.PI/2) * (180/Math.PI);
  245. },
  246. vectorDeter: function(v1, v2) {
  247. return (v1.x * v2.y)-(v1.y * v2.x);
  248. },
  249. btwn: function(a, b1, b2, round = false) {
  250. if (round) {
  251. a = Math.round(a);
  252. b1 = Math.round(b1);
  253. b2 = Math.round(b2);
  254. }
  255. if ((a >= b1) && (a <= b2)) { return true; }
  256. if ((a >= b2) && (a <= b1)) { return true; }
  257. return false;
  258. },
  259. nearPointFromPath: function(Pathsvg, point, range = Infinity) {
  260. var pathLength = Pathsvg.getTotalLength();
  261. if (pathLength>0) {
  262. var precision = 40;
  263. var best;
  264. var bestLength;
  265. var bestDistance = Infinity;
  266. for (var scan, scanLength = 0, scanDistance; scanLength <= pathLength; scanLength += precision) {
  267. scan = Pathsvg.getPointAtLength(scanLength);
  268. scanDistance = qSVG.gap(scan, point);
  269. if (scanDistance < bestDistance) {
  270. best = scan, bestLength = scanLength, bestDistance = scanDistance;
  271. }
  272. }
  273. // binary search for precise estimate
  274. precision /= 2;
  275. while (precision > 1) {
  276. var before,
  277. after,
  278. beforeLength,
  279. afterLength,
  280. beforeDistance,
  281. afterDistance;
  282. if ((beforeLength = bestLength - precision) >= 0 && (beforeDistance = qSVG.gap(before = Pathsvg.getPointAtLength(beforeLength), point)) < bestDistance) {
  283. best = before, bestLength = beforeLength, bestDistance = beforeDistance;
  284. } else if ((afterLength = bestLength + precision) <= pathLength && (afterDistance = qSVG.gap(after = Pathsvg.getPointAtLength(afterLength), point)) < bestDistance) {
  285. best = after, bestLength = afterLength, bestDistance = afterDistance;
  286. } else {
  287. precision /= 2;
  288. }
  289. }
  290. if (bestDistance <= (range*range)) {
  291. return ({
  292. x: best.x,
  293. y: best.y,
  294. length: bestLength,
  295. distance: bestDistance,
  296. seg: Pathsvg.getPathSegAtLength(bestLength)
  297. });
  298. } else {
  299. return false;
  300. }
  301. }else {
  302. return false;
  303. }
  304. },
  305. // ON PATH RETURN FALSE IF 0 NODE ON PATHSVG WITH POINT coords
  306. // RETURN INDEX ARRAY OF NODEs onPoint
  307. getNodeFromPath: function(Pathsvg, point, except = ['']) {
  308. var nodeList = Pathsvg.getPathData();
  309. var k = 0;
  310. var nodes = [];
  311. var countNode = 0;
  312. for (k = 0; k < nodeList.length; k++) {
  313. if (nodeList[k].values[0] == point.x && nodeList[k].values[1] == point.y && nodeList[k].type != 'Z') {
  314. if (except.indexOf(k) == -1) {
  315. countNode++;
  316. nodes.push(k);
  317. }
  318. }
  319. }
  320. if (countNode == 0) return false;
  321. else return nodes;
  322. },
  323. // RETURN ARRAY [{x,y}, {x,y}, ...] OF REAL COORDS POLYGON INTO WALLS, THICKNESS PARAM
  324. polygonIntoWalls: function(vertex, surface) {
  325. var vertexArray = surface;
  326. var wall = [];
  327. var polygon = [];
  328. for (var rr = 0; rr < vertexArray.length; rr++) {
  329. polygon.push({x: vertex[vertexArray[rr]].x, y: vertex[vertexArray[rr]].y});
  330. }
  331. // FIND EDGE (WALLS HERE) OF THESE TWO VERTEX
  332. for (var i = 0 ; i < vertexArray.length-1; i++) {
  333. for (var segStart = 0; segStart < vertex[vertexArray[i+1]].segment.length; segStart++) {
  334. for (var segEnd = 0; segEnd < vertex[vertexArray[i]].segment.length; segEnd++) {
  335. if (vertex[vertexArray[i+1]].segment[segStart] == vertex[vertexArray[i]].segment[segEnd]) {
  336. wall.push({x1: vertex[vertexArray[i]].x, y1: vertex[vertexArray[i]].y, x2: vertex[vertexArray[i+1]].x, y2: vertex[vertexArray[i+1]].y, segment: vertex[vertexArray[i+1]].segment[segStart]});
  337. }
  338. }
  339. }
  340. }
  341. // CALC INTERSECS OF EQ PATHS OF THESE TWO WALLS.
  342. var inside = [];
  343. var outside = [];
  344. for (var i = 0; i < wall.length; i++) {
  345. var inter = [];
  346. var edge = wall[i];
  347. if (i < wall.length - 1) var nextEdge = wall[i+1];
  348. else var nextEdge = wall[0];
  349. var angleEdge = Math.atan2(edge.y2 - edge.y1, edge.x2 - edge.x1);
  350. var angleNextEdge = Math.atan2(nextEdge.y2 - nextEdge.y1, nextEdge.x2 - nextEdge.x1);
  351. var edgeThicknessX = (WALLS[edge.segment].thick/2) * Math.sin(angleEdge);
  352. var edgeThicknessY = (WALLS[edge.segment].thick/2) * Math.cos(angleEdge);
  353. var nextEdgeThicknessX = (WALLS[nextEdge.segment].thick/2) * Math.sin(angleNextEdge);
  354. var nextEdgeThicknessY = (WALLS[nextEdge.segment].thick/2) * Math.cos(angleNextEdge);
  355. var eqEdgeUp = qSVG.createEquation(edge.x1 + edgeThicknessX, edge.y1 - edgeThicknessY, edge.x2 + edgeThicknessX, edge.y2 - edgeThicknessY);
  356. var eqEdgeDw = qSVG.createEquation(edge.x1 - edgeThicknessX, edge.y1 + edgeThicknessY, edge.x2 - edgeThicknessX, edge.y2 + edgeThicknessY);
  357. var eqNextEdgeUp = qSVG.createEquation(nextEdge.x1 + nextEdgeThicknessX, nextEdge.y1 - nextEdgeThicknessY, nextEdge.x2 + nextEdgeThicknessX, nextEdge.y2 - nextEdgeThicknessY);
  358. var eqNextEdgeDw = qSVG.createEquation(nextEdge.x1 - nextEdgeThicknessX, nextEdge.y1 + nextEdgeThicknessY, nextEdge.x2 - nextEdgeThicknessX, nextEdge.y2 + nextEdgeThicknessY);
  359. angleEdge = angleEdge * (180 / Math.PI);
  360. angleNextEdge = angleNextEdge * (180 / Math.PI);
  361. if (eqEdgeUp.A != eqNextEdgeUp.A) {
  362. inter.push(qSVG.intersectionOfEquations(eqEdgeUp, eqNextEdgeUp, "object"));
  363. inter.push(qSVG.intersectionOfEquations(eqEdgeDw, eqNextEdgeDw, "object"));
  364. }
  365. else {
  366. inter.push({x: edge.x2 + edgeThicknessX, y: edge.y2 - edgeThicknessY});
  367. inter.push({x: edge.x2 - edgeThicknessX, y: edge.y2 + edgeThicknessY});
  368. }
  369. for (var ii = 0;ii < inter.length; ii++) {
  370. if (qSVG.rayCasting(inter[ii], polygon)) inside.push(inter[ii]);
  371. else outside.push(inter[ii]);
  372. }
  373. }
  374. inside.push(inside[0]);
  375. outside.push(outside[0]);
  376. return {inside: inside, outside: outside};
  377. },
  378. area: function(coordss) {
  379. if (coordss.length < 2) return false;
  380. var realArea = 0;
  381. var j = (coordss.length)-1;
  382. for (var i = 0; i < coordss.length; i++) {
  383. realArea = realArea + ((coordss[j].x + coordss[i].x) * (coordss[j].y - coordss[i].y));
  384. j = i;
  385. }
  386. realArea = realArea / 2;
  387. return Math.abs(realArea.toFixed(2));
  388. },
  389. areaRoom: function (vertex, coords, digit = 2) {
  390. var vertexArray = coords;
  391. var roughArea = 0;
  392. var j = (vertexArray.length)-2;
  393. for (var i = 0; i < vertexArray.length-1; i++) {
  394. roughArea = roughArea + ((vertex[vertexArray[j]].x + vertex[vertexArray[i]].x) * (vertex[vertexArray[j]].y - vertex[vertexArray[i]].y));
  395. j = i;
  396. }
  397. roughArea = roughArea / 2;
  398. return Math.abs(roughArea.toFixed(digit));
  399. },
  400. perimeterRoom: function (coords, digit = 2) {
  401. var vertexArray = coords;
  402. var roughRoom = 0;
  403. for (i = 0; i < vertexArray.length-1; i++) {
  404. added = qSVG.measure(vertex[vertexArray[i]], vertex[vertexArray[i+1]]);
  405. roughRoom = roughRoom + added;
  406. }
  407. return roughRoom.toFixed(digit);
  408. },
  409. // H && V PROBLEM WHEN TWO SEGMENT ARE v/-> == I/->
  410. junctionList: function(WALLS) {
  411. var junction = [];
  412. var segmentJunction = [];
  413. var junctionChild = [];
  414. // JUNCTION ARRAY LIST ALL SEGMENT INTERSECTIONS
  415. for (var i = 0; i < WALLS.length; i++) {
  416. var equation1 = qSVG.createEquation(WALLS[i].start.x, WALLS[i].start.y, WALLS[i].end.x, WALLS[i].end.y);
  417. for (var v = 0; v < WALLS.length; v++) {
  418. if (v != i) {
  419. var equation2 = qSVG.createEquation(WALLS[v].start.x, WALLS[v].start.y, WALLS[v].end.x, WALLS[v].end.y);
  420. var intersec;
  421. if (intersec = qSVG.intersectionOfEquations(equation1, equation2)) {
  422. if (WALLS[i].end.x == WALLS[v].start.x && WALLS[i].end.y == WALLS[v].start.y || WALLS[i].start.x == WALLS[v].end.x && WALLS[i].start.y == WALLS[v].end.y) {
  423. if (WALLS[i].end.x == WALLS[v].start.x && WALLS[i].end.y == WALLS[v].start.y) {
  424. junction.push({segment:i, child: v, values: [WALLS[v].start.x, WALLS[v].start.y], type: "natural"});
  425. }
  426. if (WALLS[i].start.x == WALLS[v].end.x && WALLS[i].start.y == WALLS[v].end.y) {
  427. junction.push({segment:i, child: v, values: [WALLS[i].start.x, WALLS[i].start.y], type: "natural"});
  428. }
  429. }
  430. else {
  431. if (qSVG.btwn(intersec[0], WALLS[i].start.x, WALLS[i].end.x, 'round') && qSVG.btwn(intersec[1], WALLS[i].start.y, WALLS[i].end.y, 'round') && qSVG.btwn(intersec[0], WALLS[v].start.x, WALLS[v].end.x, 'round') && qSVG.btwn(intersec[1], WALLS[v].start.y, WALLS[v].end.y, 'round')) {
  432. intersec[0] = intersec[0];
  433. intersec[1] = intersec[1];
  434. junction.push({segment:i, child: v, values: [intersec[0], intersec[1]], type: "intersection"});
  435. }
  436. }
  437. }
  438. // IF EQ1 == EQ 2 FIND IF START OF SECOND SEG == END OF FIRST seg (eq.A maybe values H ou V)
  439. if ((Math.abs(equation1.A) == Math.abs(equation2.A) || equation1.A == equation2.A) && equation1.B == equation2.B) {
  440. if (WALLS[i].end.x == WALLS[v].start.x && WALLS[i].end.y == WALLS[v].start.y) {
  441. junction.push({segment:i, child: v, values: [WALLS[v].start.x, WALLS[v].start.y], type: "natural"});
  442. }
  443. if (WALLS[i].start.x == WALLS[v].end.x && WALLS[i].start.y == WALLS[v].end.y) {
  444. junction.push({segment:i, child: v, values: [WALLS[i].start.x, WALLS[i].start.y], type: "natural"});
  445. }
  446. }
  447. }
  448. }
  449. }
  450. return junction;
  451. },
  452. vertexList: function(junction, segment) {
  453. var vertex = [];
  454. var vertextest = [];
  455. for (var jj = 0; jj < junction.length; jj++) {
  456. var found = true;
  457. for (var vv = 0; vv < vertex.length; vv++) {
  458. if ((Math.round(junction[jj].values[0]) == Math.round(vertex[vv].x)) && (Math.round(junction[jj].values[1]) == Math.round(vertex[vv].y))) {
  459. found = false;
  460. vertex[vv].segment.push(junction[jj].segment);
  461. break;
  462. }
  463. else {
  464. found = true;
  465. }
  466. }
  467. if (found) {
  468. vertex.push({x: Math.round(junction[jj].values[0]), y: Math.round(junction[jj].values[1]), segment: [junction[jj].segment], bypass:0, type: junction[jj].type});
  469. }
  470. }
  471. var toClean = [];
  472. for (var ss = 0; ss < vertex.length; ss++) {
  473. vertex[ss].child = [];
  474. vertex[ss].removed = [];
  475. for (var sg = 0; sg < vertex[ss].segment.length; sg++) {
  476. for (var sc = 0; sc < vertex.length; sc++) {
  477. if (sc != ss) {
  478. for (var scg = 0; scg < vertex[sc].segment.length; scg++) {
  479. if (vertex[sc].segment[scg] == vertex[ss].segment[sg]) {
  480. vertex[ss].child.push({id: sc, angle: Math.floor(qSVG.getAngle(vertex[ss], vertex[sc]).deg)});
  481. }
  482. }
  483. }
  484. }
  485. }
  486. toClean = [];
  487. for (var fr = 0; fr < vertex[ss].child.length-1; fr++) {
  488. for (var ft = fr+1; ft < vertex[ss].child.length; ft++) {
  489. if (fr != ft && typeof(vertex[ss].child[fr])!='undefined') {
  490. found = true;
  491. if (qSVG.btwn(vertex[ss].child[ft].angle, vertex[ss].child[fr].angle+3, vertex[ss].child[fr].angle-3, 'round') && found)
  492. {
  493. var dOne = qSVG.gap(vertex[ss], vertex[vertex[ss].child[ft].id]);
  494. var dTwo = qSVG.gap(vertex[ss], vertex[vertex[ss].child[fr].id]);
  495. if (dOne > dTwo) {
  496. toClean.push(ft);
  497. }
  498. else {
  499. toClean.push(fr);
  500. }
  501. }
  502. }
  503. }
  504. }
  505. toClean.sort(function(a, b) {
  506. return b-a;
  507. });
  508. toClean.push(-1);
  509. for (var cc = 0; cc < toClean.length-1; cc++) {
  510. if (toClean[cc] > toClean[(cc+1)]) {
  511. vertex[ss].removed.push(vertex[ss].child[toClean[cc]].id);
  512. vertex[ss].child.splice(toClean[cc], 1);
  513. }
  514. }
  515. }
  516. vertexTest = vertex;
  517. return vertex;
  518. },
  519. //*******************************************************
  520. //* @arr1, arr2 = Array to compare *
  521. //* @app = add function pop() or shift() to @arr1, arr2 *
  522. //* False if arr1.length != arr2.length *
  523. //* False if value into arr1[] != arr2[] - no order *
  524. //* *****************************************************
  525. arrayCompare: function(arr1, arr2, app) {
  526. // if (arr1.length != arr2.length) return false;
  527. var minus = 0;
  528. var start = 0;
  529. if (app == 'pop') {
  530. minus = 1;
  531. }
  532. if (app == 'shift') {
  533. start = 1;
  534. }
  535. var coordCounter = arr1.length - minus - start;
  536. for (var iFirst = start; iFirst < arr1.length-minus; iFirst++) {
  537. for (var iSecond = start; iSecond < arr2.length-minus; iSecond++) {
  538. if (arr1[iFirst] == arr2[iSecond]) {
  539. coordCounter--;
  540. }
  541. }
  542. }
  543. if (coordCounter == 0) return true;
  544. else return false;
  545. },
  546. vectorVertex: function(vex1, vex2, vex3) {
  547. var vCurr = qSVG.vectorXY(vex1, vex2);
  548. var vNext = qSVG.vectorXY(vex2, vex3);
  549. var Na = Math.sqrt((vCurr.x * vCurr.x) + (vCurr.y * vCurr.y));
  550. var Nb = Math.sqrt((vNext.x * vNext.x) + (vNext.y * vNext.y));
  551. var C = ((vCurr.x * vNext.x) + (vCurr.y * vNext.y)) / (Na * Nb);
  552. var S = ((vCurr.x * vNext.y) - (vCurr.y * vNext.x));
  553. var BAC = Math.sign(S) * Math.acos(C);
  554. return BAC*(180 / Math.PI );
  555. },
  556. segmentTree: function(VERTEX_NUMBER, vertex) {
  557. var TREELIST = [VERTEX_NUMBER];
  558. WAY = [];
  559. var COUNT = vertex.length;
  560. var ORIGIN = VERTEX_NUMBER;
  561. tree(TREELIST, ORIGIN, COUNT);
  562. return WAY;
  563. function tree(TREELIST, ORIGIN, COUNT) {
  564. if (TREELIST.length == 0) return;
  565. var TREETEMP = [];
  566. COUNT--;
  567. for (var k = 0;k < TREELIST.length; k++) {
  568. var found = true;
  569. var WRO = TREELIST[k];
  570. var WRO_ARRAY = WRO.toString().split('-');
  571. var WR = WRO_ARRAY[WRO_ARRAY.length - 1];
  572. for (var v = 0; v < vertex[WR].child.length; v++) {
  573. if (vertex[WR].child[v].id == ORIGIN && COUNT < (vertex.length - 1) && WRO_ARRAY.length > 2) { // WAYS HYPER
  574. WAY.push(WRO+"-"+ORIGIN); // WAYS
  575. found = false;
  576. break;
  577. }
  578. }
  579. if (found) {
  580. var bestToAdd;
  581. var bestDet = 0;
  582. var nextVertex = -1;
  583. // var nextVertexValue = 360;
  584. var nextDeterValue = Infinity;
  585. var nextDeterVal = 0;
  586. var nextFlag = 0;
  587. if (vertex[WR].child.length == 1) {
  588. if (WR == ORIGIN && COUNT == (vertex.length - 1)) {
  589. TREETEMP.push(WRO+'-'+vertex[WR].child[0].id);
  590. }
  591. if (WR != ORIGIN && COUNT < (vertex.length - 1)) {
  592. TREETEMP.push(WRO+'-'+vertex[WR].child[0].id);
  593. }
  594. }
  595. else {
  596. for (var v = 0; v < vertex[WR].child.length && vertex[WR].child.length > 0; v++) {
  597. if (WR == ORIGIN && COUNT == (vertex.length - 1)) { // TO INIT FUNCTION -> // CLOCKWISE Research
  598. var vDet = qSVG.vectorVertex({x: 0, y: -1}, vertex[WR], vertex[vertex[WR].child[v].id]);
  599. if (vDet >= nextDeterVal ) {
  600. nextFlag = 1;
  601. nextDeterVal = vDet;
  602. nextVertex = vertex[WR].child[v].id;
  603. }
  604. if (Math.sign(vDet) == -1 && nextFlag == 0) {
  605. if (vDet < nextDeterValue && Math.sign(nextDeterValue) > -1) {
  606. nextDeterValue = vDet;
  607. nextVertex = vertex[WR].child[v].id;
  608. }
  609. if (vDet > nextDeterValue && Math.sign(nextDeterValue) == -1) {
  610. nextDeterValue = vDet;
  611. nextVertex = vertex[WR].child[v].id;
  612. }
  613. }
  614. }
  615. if (WR != ORIGIN && WRO_ARRAY[WRO_ARRAY.length-2] != vertex[WR].child[v].id && COUNT < (vertex.length - 1)) { // COUNTERCLOCKWISE Research
  616. var vDet = qSVG.vectorVertex(vertex[WRO_ARRAY[WRO_ARRAY.length-2]], vertex[WR], vertex[vertex[WR].child[v].id]);
  617. if (vDet < nextDeterValue && nextFlag == 0) {
  618. nextDeterValue = vDet;
  619. nextVertex = vertex[WR].child[v].id;
  620. }
  621. if (Math.sign(vDet) == -1) {
  622. nextFlag = 1;
  623. if (vDet <= nextDeterValue) {
  624. nextDeterValue = vDet;
  625. nextVertex = vertex[WR].child[v].id;
  626. }
  627. }
  628. }
  629. }
  630. if (nextVertex != -1) TREETEMP.push(WRO+'-'+nextVertex);
  631. }
  632. }
  633. }
  634. if (COUNT > 0) tree(TREETEMP, ORIGIN, COUNT);
  635. }
  636. },
  637. polygonize: function(segment) {
  638. junction = qSVG.junctionList(segment);
  639. vertex = qSVG.vertexList(junction, segment);
  640. var vertexCopy = qSVG.vertexList(junction, segment);
  641. var edgesChild = [];
  642. for (var j = 0; j < vertex.length; j++) {
  643. for (var vv = 0; vv < vertex[j].child.length; vv++) {
  644. edgesChild.push([j, vertex[j].child[vv].id]);
  645. }
  646. }
  647. var polygons = [];
  648. var WAYS;
  649. for (var jc = 0; jc < edgesChild.length; jc++) {
  650. var bestVertex = 0;
  651. var bestVertexValue = Infinity;
  652. for (var j = 0; j < vertex.length; j++) {
  653. if (vertex[j].x < bestVertexValue && vertex[j].child.length > 1 && vertex[j].bypass == 0) {
  654. bestVertexValue = vertex[j].x;
  655. bestVertex = j;
  656. }
  657. if (vertex[j].x == bestVertexValue && vertex[j].child.length > 1 && vertex[j].bypass == 0) {
  658. if (vertex[j].y > vertex[bestVertex].y) {
  659. bestVertexValue = vertex[j].x;
  660. bestVertex = j;
  661. }
  662. }
  663. }
  664. // console.log("%c%s", "background: yellow; font-size: 14px;","RESEARCH WAY FOR STARTING VERTEX "+bestVertex);
  665. WAYS = qSVG.segmentTree(bestVertex, vertex);
  666. if (WAYS.length == 0) {
  667. vertex[bestVertex].bypass = 1;
  668. }
  669. if (WAYS.length > 0) {
  670. var tempSurface = WAYS[0].split('-');
  671. var lengthRoom = qSVG.areaRoom(vertex, tempSurface);
  672. var bestArea = parseInt(lengthRoom);
  673. var found = true;
  674. for (var sss = 0; sss < polygons.length; sss++) {
  675. if (qSVG.arrayCompare(polygons[sss].way, tempSurface, 'pop') ) {
  676. found = false;
  677. vertex[bestVertex].bypass = 1;
  678. break;
  679. }
  680. }
  681. if (bestArea < 360) {
  682. vertex[bestVertex].bypass = 1;
  683. }
  684. if (vertex[bestVertex].bypass == 0) { // <-------- TO REVISE IMPORTANT !!!!!!!! bestArea Control ???
  685. var realCoords = qSVG.polygonIntoWalls(vertex, tempSurface);
  686. var realArea = qSVG.area(realCoords.inside);
  687. var outsideArea = qSVG.area(realCoords.outside);
  688. var coords = [];
  689. for (var rr = 0; rr < tempSurface.length; rr++) {
  690. coords.push({x: vertex[tempSurface[rr]].x, y: vertex[tempSurface[rr]].y});
  691. }
  692. // WARNING -> FAKE
  693. if (realCoords.inside.length != realCoords.outside) {
  694. polygons.push({way: tempSurface, coords: coords, coordsOutside: realCoords.outside, coordsInside: realCoords.inside, area: realArea, outsideArea: outsideArea, realArea: bestArea});
  695. }
  696. else { // REAL INSIDE POLYGONE -> ROOM
  697. polygons.push({way: tempSurface, coords: realCoords.inside, coordsOutside: realCoords.outside, area: realArea, outsideArea: outsideArea, realArea: bestArea});
  698. }
  699. // REMOVE FIRST POINT OF WAY ON CHILDS FIRST VERTEX
  700. for (var aa = 0; aa < vertex[bestVertex].child.length; aa++) {
  701. if (vertex[bestVertex].child[aa].id == tempSurface[1]) {
  702. vertex[bestVertex].child.splice(aa, 1);
  703. }
  704. }
  705. // REMOVE FIRST VERTEX OF WAY ON CHILDS SECOND VERTEX
  706. for (var aa = 0; aa < vertex[tempSurface[1]].child.length; aa++) {
  707. if (vertex[tempSurface[1]].child[aa].id == bestVertex) {
  708. vertex[tempSurface[1]].child.splice(aa, 1);
  709. }
  710. }
  711. //REMOVE FILAMENTS ?????
  712. do {
  713. var looping = 0;
  714. for (var aa = 0; aa < vertex.length; aa++) {
  715. if (vertex[aa].child.length == 1) {
  716. looping = 1;
  717. vertex[aa].child = [];
  718. for (var ab = 0; ab < vertex.length; ab++) { // OR MAKE ONLY ON THE WAY tempSurface ?? BETTER ??
  719. for (var ac = 0; ac < vertex[ab].child.length; ac++) {
  720. if (vertex[ab].child[ac].id == aa) {
  721. vertex[ab].child.splice(ac, 1);
  722. }
  723. }
  724. }
  725. }
  726. }
  727. }
  728. while (looping == 1);
  729. }
  730. }
  731. }
  732. //SUB AREA(s) ON POLYGON CONTAINS OTHERS FREE POLYGONS (polygon without commonSideEdge)
  733. for (var pp = 0; pp < polygons.length; pp++) {
  734. var inside = [];
  735. for (var free = 0; free < polygons.length; free++) {
  736. if (pp != free) {
  737. var polygonFree = polygons[free].coords;
  738. var countCoords = polygonFree.length;
  739. var found = true;
  740. for (pf = 0; pf < countCoords; pf++) {
  741. found = qSVG.rayCasting(polygonFree[pf], polygons[pp].coords);
  742. if (!found) {
  743. break;
  744. }
  745. }
  746. if (found) {
  747. inside.push(free);
  748. polygons[pp].area = polygons[pp].area - polygons[free].outsideArea;
  749. }
  750. }
  751. }
  752. polygons[pp].inside = inside;
  753. }
  754. return {polygons : polygons, vertex : vertex};
  755. },
  756. diffArray : function(arr1, arr2) {
  757. return arr1.concat(arr2).filter(function (val) {
  758. if (!(arr1.includes(val) && arr2.includes(val)))
  759. return val;
  760. });
  761. },
  762. diffObjIntoArray : function(arr1, arr2) {
  763. var count = 0;
  764. for (var k =0; k <arr1.length-1;k++) {
  765. for (var n=0; n<arr2.length-1;n++) {
  766. if (isObjectsEquals(arr1[k], arr2[n])) {
  767. count++;
  768. }
  769. }
  770. }
  771. var waiting = arr1.length-1;
  772. if (waiting < arr2.length-1) waiting = arr2.length;
  773. return waiting-count;
  774. },
  775. rayCasting: function(point, polygon) {
  776. var x = point.x, y = point.y;
  777. var inside = false;
  778. for (var i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
  779. var xi = polygon[i].x, yi = polygon[i].y;
  780. var xj = polygon[j].x, yj = polygon[j].y;
  781. var intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi);
  782. if (intersect) inside = !inside;
  783. }
  784. return inside;
  785. },
  786. //polygon = [{x1,y1}, {x2,y2}, ...]
  787. polygonVisualCenter: function(room) {
  788. var polygon = room.coords;
  789. var insideArray = room.inside;
  790. var sample = 80;
  791. var grid = [];
  792. //BOUNDING BOX OF POLYGON
  793. var minX, minY, maxX, maxY;
  794. for (var i = 0; i < polygon.length; i++) {
  795. var p = polygon[i];
  796. if (!i || p.x < minX) minX = p.x;
  797. if (!i || p.y < minY) minY = p.y;
  798. if (!i || p.x > maxX) maxX = p.x;
  799. if (!i || p.y > maxY) maxY = p.y;
  800. }
  801. var width = maxX - minX;
  802. var height = maxY - minY;
  803. //INIT GRID
  804. var sampleWidth = Math.floor(width / sample);
  805. var sampleHeight = Math.floor(height / sample);
  806. for (var hh = 0; hh < sample; hh++) {
  807. for (var ww = 0; ww < sample; ww++) {
  808. var posX = minX + (ww * sampleWidth);
  809. var posY = minY + (hh * sampleHeight);
  810. if (qSVG.rayCasting({x: posX, y: posY}, polygon)) {
  811. var found = true;
  812. for (var ii = 0; ii < insideArray.length; ii++) {
  813. if (qSVG.rayCasting({x: posX, y: posY}, ROOM[insideArray[ii]].coordsOutside)) {
  814. found = false;
  815. break;
  816. }
  817. }
  818. if (found) {
  819. grid.push({x: posX, y: posY});
  820. }
  821. }
  822. }
  823. }
  824. var bestRange = 0;
  825. var bestMatrix;
  826. for (var matrix = 0; matrix < grid.length; matrix++) {
  827. var minDistance = Infinity;
  828. for (var pp = 0; pp < polygon.length-1; pp++) {
  829. var scanDistance = qSVG.pDistance(grid[matrix], polygon[pp], polygon[pp+1]);
  830. if (scanDistance.distance < minDistance) {
  831. minDistance = scanDistance.distance;
  832. }
  833. }
  834. if (minDistance > bestRange) {
  835. bestMatrix = matrix;
  836. bestRange = minDistance;
  837. }
  838. }
  839. return grid[bestMatrix];
  840. },
  841. textOnDiv: function(label, pos, styled, div) {
  842. if (typeof(pos) != 'undefined') {
  843. var text = document.createElementNS('http://www.w3.org/2000/svg', 'text');
  844. text.setAttributeNS(null, 'x', pos.x);
  845. text.setAttributeNS(null, 'y', pos.y);
  846. text.setAttribute("style","fill:"+styled.color+";font-weight:"+styled.fontWeight+";font-size:"+styled.fontSize);
  847. text.setAttributeNS(null, 'text-anchor', 'middle');
  848. text.textContent = label;
  849. document.getElementById(div).appendChild(text);
  850. }
  851. }
  852. };
  853. //----------------------- END Quick SVG LIBRARY --------------------------------------------------s