CAD.js 17 KB

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