CAD.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538
  1. var grendCAD = (function grentWall() {
  2. var util = {
  3. /**
  4. * 获取一组坐标的XY坐标区间
  5. * @param {*} points
  6. */
  7. getSection (points) {
  8. var ret = {
  9. minx: points[0].x,
  10. maxx: points[0].x,
  11. miny: points[0].y,
  12. maxy: points[0].y
  13. }
  14. points.forEach(function (point) {
  15. if (ret.minx > point.x) {
  16. ret.minx = point.x
  17. }
  18. if (ret.maxx < point.x) {
  19. ret.maxx = point.x
  20. }
  21. if (ret.miny > point.y) {
  22. ret.miny = point.y
  23. }
  24. if (ret.maxy < point.y) {
  25. ret.maxy = point.y
  26. }
  27. })
  28. return ret
  29. },
  30. /**
  31. * 计算垂直向量
  32. * @param {向量} arr
  33. */
  34. verticalLine (arr) {
  35. if (arr.length !== 0) {
  36. var x, y;
  37. y = Math.sqrt(1 / ((arr[1] * arr[1]) / (arr[0] * arr[0]) + 1));
  38. x = Math.sqrt(1 - y * y);
  39. return [x, y];
  40. }
  41. return [];
  42. },
  43. /**
  44. * 将所有坐标转化为指定范围之间的值,但是保持比例不变
  45. * @param {*} points 要转化的坐标
  46. * @param {*} section 指定范围
  47. * @returns Array 转化后的值
  48. */
  49. tranProp (points, section) {
  50. var args = util.getSection(points)
  51. var xlen = args.maxx - args.minx
  52. var ylen = args.maxy - args.miny
  53. var prop = xlen > ylen ? xlen : ylen
  54. var offsetX = -(args.minx + xlen / 2)
  55. var offsetY = -(args.miny + ylen / 2)
  56. var range = section[1] - section[0]
  57. return {
  58. offset: {
  59. x: offsetX,
  60. y: offsetY
  61. },
  62. prop,
  63. range: range
  64. }
  65. },
  66. /**
  67. * 将屏幕转化为真实坐标
  68. * @param {*} point
  69. * @param {*} section
  70. */
  71. tranPoint (point, section) {
  72. return {
  73. x: point.x + section.minx,
  74. y: point.y + section.miny
  75. }
  76. },
  77. /**
  78. * 判断点是否在线上
  79. * @param {*} point 要判断的点
  80. * @param {*} line 线段
  81. * @param {*} width 线宽
  82. */
  83. isContainPoint (point, line, width) {
  84. var dis = 0
  85. var s1 = line[1].x - line[0].x
  86. var s2 = point.x - line[0].x
  87. var s3 = point.x - line[1].x
  88. var k1 = line[1].y - line[0].y
  89. var k2 = point.y - line[0].y
  90. var k3 = point.y - line[1].y
  91. var max = width / 2
  92. var min = 0 - max
  93. var cross = s1 * s2 + k1 * k2;
  94. var d2 = s1 * s1 + k1 * k1;
  95. if (cross <= 0) {
  96. dis = Math.sqrt(s2 * s2 + k2 * k2);
  97. } else if (cross >= d2) {
  98. dis = Math.sqrt(s3 * s3 + k3 * k3);
  99. } else {
  100. var r = cross / d2;
  101. var px = line[0].x + s1 * r;
  102. var py = line[0].y + k1 * r;
  103. dis = Math.sqrt((point.x - px) * (point.x - px) + (py - point.y) * (py - point.y));
  104. }
  105. return dis >= min && dis <= max
  106. },
  107. /**
  108. * 判断两条线段是否相交
  109. * @param {*} line1
  110. * @param {*} line2
  111. */
  112. isLineIntersect (line1, line2) {
  113. var a1 = line1[1].y - line1[0].y;
  114. var b1 = line1[0].x - line1[1].x;
  115. var c1 = a1 * line1[0].x + b1 * line1[0].y;
  116. //转换成一般式: Ax+By = C
  117. var a2 = line2[1].y - line2[0].y;
  118. var b2 = line2[0].x - line2[1].x;
  119. var c2 = a2 * line2[0].x + b2 * line2[0].y;
  120. // 计算交点
  121. var d = a1 * b2 - a2 * b1;
  122. // 当d==0时,两线平行
  123. if (d == 0) {
  124. return false;
  125. } else {
  126. var x = (b2 * c1 - b1 * c2) / d;
  127. var y = (a1 * c2 - a2 * c1) / d;
  128. // 检测交点是否在两条线段上
  129. if ((isInBetween(line1[0].x, x, line1[1].x) || isInBetween(line1[0].y, y, line1[1].y)) &&
  130. (isInBetween(line2[0].x, x, line2[1].x) || isInBetween(line2[0].y, y, line2[1].y))) {
  131. return true;
  132. }
  133. }
  134. function isInBetween(a, b, c) {
  135. // 如果b几乎等于a或c,返回false.为了避免浮点运行时两值几乎相等,但存在相差0.00000...0001的这种情况出现使用下面方式进行避免
  136. if (Math.abs(a - b) < 0.000001 || Math.abs(b - c) < 0.000001) {
  137. return false;
  138. }
  139. return (a <= b && b <= c) || (c <= b && b <= a);
  140. }
  141. return false;
  142. },
  143. /**
  144. * 判断两个点是否相同
  145. * @param {*} point1
  146. * @param {*} poin2
  147. */
  148. equalPoint (point1, poin2) {
  149. return point1.x === poin2.x && point1.y === poin2.y
  150. },
  151. /**
  152. * 判断两个面是否相交,
  153. * @param {*} face1
  154. * @param {*} face2
  155. */
  156. isFaceIntersect(face1, face2) {
  157. for (var i = 0; i < face1.length; i++) {
  158. var next = i + 1 === face1.length ? 0 : i + 1
  159. var line1 = [face1[i], face1[next]]
  160. for (var j = 0; j < face2.length; j++) {
  161. var next = j + 1 === face2.length ? 0 : j + 1
  162. var line2 = [face2[j], face2[next]]
  163. var isIntersect1 = util.isLineIntersect(line2, line1)
  164. var isIntersect2 = util.isLineIntersect(line1, line2)
  165. if (isIntersect1 && isIntersect2) {
  166. return true
  167. }
  168. }
  169. }
  170. },
  171. /**
  172. * 判断一个点是否在面上
  173. * @param {*} face1
  174. * @param {*} face2
  175. */
  176. pointInside(point, face) {
  177. var inside = false;
  178. var x = point.x,
  179. y = point.y;
  180. for (var i = 0, j = face.length - 1; i < face.length; j = i++) {
  181. var xi = face[i].x,
  182. yi = face[i].y;
  183. var xj = face[j].x,
  184. yj = face[j].y;
  185. if(((yi > y) != (yj > y)) &&
  186. (x < (xj - xi) * (y - yi) / (yj - yi) + xi)) {
  187. inside = !inside;
  188. }
  189. }
  190. return inside;
  191. }
  192. }
  193. var drawShape = {
  194. line (context, line, width) {
  195. context.strokeStyle = 'red';
  196. context.lineWidth = width;
  197. context.beginPath();
  198. context.moveTo(line[0].x, line[0].y);
  199. context.lineTo(line[1].x, line[1].y);
  200. context.closePath();
  201. context.stroke();
  202. },
  203. camera (context, point, width) {
  204. context.fillStyle = "#00cc00";
  205. context.fillRect(point.x - width / 2, point.y - width / 2, width, width);
  206. }
  207. }
  208. /**
  209. * 获取面的所有点
  210. * @param {*} face
  211. */
  212. function getFacePoints(face) {
  213. var start = face[0]
  214. var ret = []
  215. var iteration = start
  216. do {
  217. ret.push(iteration.points[0])
  218. iteration = face.find(function (item) {
  219. return item.id === iteration.linkedLineID[1]
  220. })
  221. } while (start !== iteration)
  222. return ret
  223. }
  224. /**
  225. * 获取一个元素在指定父元素或文档中的位置
  226. */
  227. function getPosition(dom, parent) {
  228. var ret = {
  229. x: 0,
  230. y: 0
  231. }
  232. while (dom === parent || dom === document.documentElement) {
  233. ret.x += dom.offsetLeft
  234. ret.y += dom.offsetTop
  235. dom = dom.offsetParent
  236. }
  237. return ret
  238. }
  239. function addMouseEvent (dom, event) {
  240. dom.addEventListener('mousedown', function downHandle(ev) {
  241. var prevPoint = {x: ev.offsetX, y: ev.offsetY}
  242. var con = event.down(prevPoint)
  243. if (!con) return;
  244. document.documentElement.addEventListener('mousemove', moveHandle, {passive: false})
  245. document.documentElement.addEventListener('mouseup', endHandle, {passive: false})
  246. function moveHandle (ev) {
  247. event.move({x: ev.offsetX, y: ev.offsetY}, prevPoint)
  248. prevPoint = { x: ev.offsetX, y: ev.offsetY }
  249. }
  250. function endHandle (ev) {
  251. document.documentElement.removeEventListener('mousemove', moveHandle, {passive: false})
  252. document.documentElement.removeEventListener('mouseup', endHandle, {passive: false})
  253. event.up()
  254. }
  255. }, false)
  256. }
  257. function Wall(canvas, data, cameras, cal) {
  258. this.width = canvas.width
  259. this.height = canvas.height
  260. this.canvas = canvas
  261. this.context = canvas.getContext('2d')
  262. this.origin = data
  263. this.context.translate(this.width / 2, this.height / 2);
  264. this.screenOffset = getPosition(this.canvas)
  265. this.lineWidth = 2
  266. this.cameras = cameras
  267. this.cal = cal
  268. this.init()
  269. this.draw()
  270. }
  271. Wall.prototype.init = function () {
  272. var points = []
  273. var faces = {}
  274. this.origin.forEach(function(line) {
  275. if (faces[line.roomIndex]) {
  276. faces[line.roomIndex].push(line)
  277. } else {
  278. faces[line.roomIndex] = [line]
  279. }
  280. points = points.concat(...line.points)
  281. })
  282. var result = util.tranProp(points, [-0.99, 0.99])
  283. this.status = {
  284. offset: result.offset,
  285. prop: result.prop,
  286. range: result.range
  287. }
  288. this.faces = faces
  289. this.regEvent()
  290. }
  291. Wall.prototype.transScreenPoint = function (point) {
  292. var spoint = {x: point.x, y: point.y}
  293. spoint.x += this.status.offset.x
  294. spoint.y += this.status.offset.y
  295. spoint.x = (spoint.x * this.status.range / this.status.prop) * (this.width / 2)
  296. spoint.y = (spoint.y * this.status.range / this.status.prop) * (this.height / 2)
  297. return spoint
  298. }
  299. Wall.prototype.transRealPoint = function (point) {
  300. return {
  301. x: (point.x * this.status.prop) / (this.status.range * this.width / 2) - this.status.offset.x,
  302. y: (point.y * this.status.prop) / (this.status.range * this.width / 2) - this.status.offset.y
  303. }
  304. }
  305. Wall.prototype.regEvent = function () {
  306. var sinfo;
  307. var section = {
  308. minx: -this.width / 2,
  309. maxx: this.width / 2,
  310. miny: -this.height / 2,
  311. maxy: this.height / 2
  312. }
  313. var startPoint = null
  314. var dirct = null
  315. var event = {
  316. down (offset) {
  317. startPoint = this.transRealPoint(util.tranPoint(offset, section))
  318. sinfo = this._down(startPoint)
  319. prevPoint = startPoint
  320. return !!sinfo
  321. },
  322. move (move) {
  323. var point = this.transRealPoint(util.tranPoint(move, section))
  324. if (!dirct) {
  325. dirct = sinfo.dire[0] - sinfo.dire[1] > 0 ? 'X' : 'Y'
  326. }
  327. this._move(point, prevPoint, sinfo, dirct)
  328. prevPoint = point
  329. },
  330. up () {
  331. dirct = null
  332. startPoint = null
  333. }
  334. }
  335. event.down = event.down.bind(this)
  336. event.move = event.move.bind(this)
  337. event.up = event.up.bind(this)
  338. addMouseEvent(this.canvas, event)
  339. }
  340. Wall.prototype._down = function (point) {
  341. console.log(point)
  342. for (var key in this.faces) {
  343. var face = this.faces[key]
  344. for (var i = 0, line; line = face[i]; i++) {
  345. if (util.isContainPoint(point, line.points, 10 / this.status.prop)) {
  346. return {
  347. face: face,
  348. line: line,
  349. dire: util.verticalLine([
  350. line.points[1].x - line.points[0].x,
  351. line.points[1].y - line.points[0].y,
  352. ])
  353. }
  354. }
  355. }
  356. }
  357. }
  358. Wall.prototype.updateLine = function (line, x, y) {
  359. var face
  360. for (var key in this.faces) {
  361. var index = this.faces[key].findIndex(function (item) {
  362. return item === line
  363. })
  364. if (~index) {
  365. face = this.faces[key]
  366. break
  367. }
  368. }
  369. if (!face) return
  370. var leftPoint = face.find(function (item) {
  371. return item.id === line.linkedLineID[0]
  372. })
  373. var rightPoint = face.find(function (item) {
  374. return item.id === line.linkedLineID[1]
  375. })
  376. line.points[0].x += x
  377. line.points[0].y += y
  378. line.points[1].x += x
  379. line.points[1].y += y
  380. leftPoint.points[1].x = line.points[0].x
  381. leftPoint.points[1].y = line.points[0].y
  382. rightPoint.points[0].x = line.points[1].x
  383. rightPoint.points[0].y = line.points[1].y
  384. }
  385. Wall.prototype._move = function (point, prevPoint, movePosition, dirct) {
  386. var xdis = point.x - prevPoint.x
  387. var ydis = point.y - prevPoint.y
  388. var revoke = false
  389. if (dirct === 'X') {
  390. this.updateLine(movePosition.line, xdis, 0)
  391. } else {
  392. this.updateLine(movePosition.line, 0, ydis)
  393. }
  394. var activeFacePoint = getFacePoints(movePosition.face)
  395. for (let key in this.faces) {
  396. if (this.faces[key] !== movePosition.face &&
  397. util.isFaceIntersect(activeFacePoint, getFacePoints(this.faces[key]))) {
  398. revoke = true;
  399. break;
  400. }
  401. }
  402. var cameras;
  403. if (this.cameras && (cameras = this.cameras[movePosition.face[0].roomIndex])) {
  404. for (var i = 0, camera; camera = cameras[i]; i++) {
  405. if (!util.pointInside(camera, activeFacePoint)) {
  406. revoke = true
  407. break;
  408. }
  409. }
  410. }
  411. if (revoke) {
  412. if (dirct === 'X') {
  413. this.updateLine(movePosition.line, -xdis, 0)
  414. } else {
  415. this.updateLine(movePosition.line, 0, -ydis)
  416. }
  417. } else {
  418. this.cal && this.cal.changePoint && this.cal.changePoint(movePosition.line)
  419. }
  420. this.draw()
  421. }
  422. Wall.prototype.draw = function () {
  423. this.context.clearRect(-this.width / 2, -this.height / 2, this.width, this.height)
  424. for (var key in this.faces) {
  425. var face = this.faces[key]
  426. for (var i = 0, line; line = face[i]; i++) {
  427. drawShape.line(
  428. this.context,
  429. [
  430. this.transScreenPoint(line.points[0]),
  431. this.transScreenPoint(line.points[1])
  432. ],
  433. this.lineWidth
  434. )
  435. }
  436. }
  437. if (this.cameras) {
  438. for (var key in this.cameras) {
  439. for (var i = 0; i < this.cameras[key].length; i++) {
  440. drawShape.camera(this.context, this.transScreenPoint(this.cameras[key][i]), this.lineWidth)
  441. }
  442. }
  443. }
  444. }
  445. return function (dom, line2Ds, cameras) {
  446. window. wall = new Wall(dom, line2Ds, cameras, {
  447. changePoint: function(line2D) {
  448. if (line2D.updateLine3D) {
  449. line2D.updateLine3D()
  450. } else {
  451. console.error('当前线条没有注册3D线条方法')
  452. }
  453. }
  454. });
  455. line2Ds.forEach(function (line2D) {
  456. let fn = line2D.draw
  457. line2D.draw = function () {
  458. wall.draw()
  459. fn && fn.bind(this)();
  460. }
  461. })
  462. }
  463. })();