CAD.js 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332
  1. // 发布订阅,事件模型
  2. var Event = (function () {
  3. var _default = 'default';
  4. var _shift = Array.prototype.shift;
  5. var _unshift = Array.prototype.unshift;
  6. function createEvent() {
  7. var namespaceCache = {}
  8. function _listen(cache, key, fn) {
  9. if (cache[key]) {
  10. cache[key].push(fn)
  11. } else {
  12. cache[key] = [fn]
  13. }
  14. }
  15. function _trigger() {
  16. var args = arguments
  17. return new Promise(function (resolve) {
  18. setTimeout(function () {
  19. var cache = _shift.call(args);
  20. var key = _shift.call(args);
  21. var stack = cache[key]
  22. if (!stack || stack.length === 0) return;
  23. stack.forEach(function (fn) {
  24. fn.apply(this, args)
  25. })
  26. resolve(stack);
  27. }, 100)
  28. })
  29. }
  30. function _remove(cache, key, fn) {
  31. var stack = cache[key]
  32. if (!stack || stack.length === 0) return
  33. if (fn) {
  34. for (var i = stack.length; i >= 0; i--) {
  35. if (stack[i] === fn) {
  36. stack.splice(i, 1)
  37. }
  38. }
  39. } else {
  40. stack.length = 0
  41. }
  42. }
  43. function _create(namespace) {
  44. namespace = namespace || _default
  45. if (namespaceCache[namespace]) {
  46. return namespaceCache[namespace]
  47. }
  48. var cache = {}
  49. var ret = {
  50. listen: function (key, fn) {
  51. _listen(cache, key, fn)
  52. },
  53. once: function (key, fn) {
  54. fn.__once = true
  55. _listen(cache, key, fn)
  56. },
  57. remove: function (key, fn) {
  58. _remove(cache, key, fn)
  59. },
  60. trigger: function () {
  61. _unshift.call(arguments, cache)
  62. _trigger.apply(this, arguments).then(function (stack) {
  63. if (stack) {
  64. for (var i = stack.length; i >= 0; i--) {
  65. if (stack[i] && stack[i].__once) {
  66. stack.splice(i, 1)
  67. }
  68. }
  69. }
  70. })
  71. }
  72. }
  73. namespaceCache[namespace] = ret;
  74. return ret
  75. }
  76. return {
  77. create: _create,
  78. once: function () {
  79. this.create().once.apply(this, arguments)
  80. },
  81. listen: function () {
  82. this.create().listen.apply(this, arguments)
  83. },
  84. remove: function () {
  85. this.create().remove.apply(this, arguments)
  86. },
  87. trigger: function () {
  88. this.create().trigger.apply(this, arguments)
  89. }
  90. }
  91. }
  92. return createEvent()
  93. })();
  94. var grendCAD = (function grentWall() {
  95. var util = {
  96. /**
  97. * 计算直线向量
  98. * @param {*} line
  99. */
  100. lineVector(line) {
  101. var cpoint = {
  102. x: line.points[1].x - line.points[0].x,
  103. y: line.points[1].y - line.points[0].y
  104. }
  105. var xd = Math.abs(Math.floor(cpoint.x / 1))
  106. var yd = Math.abs(Math.floor(cpoint.y / 1))
  107. var js = xd > yd ? xd : yd
  108. var verctor = {
  109. x: cpoint.x / js,
  110. y: cpoint.y / js,
  111. }
  112. return verctor
  113. },
  114. /**
  115. * 计算直线与X轴夹角
  116. * @param {*} line1
  117. */
  118. lineAngle(line1) {
  119. var height = line1[0].y - line1[1].y,
  120. width = line1[0].x - line1[1].x;
  121. if (width == 0) {
  122. // 如果和y轴平行,角度为90或270
  123. return line1[0].y >= line1[1].y ? 90 : 270;
  124. } else {
  125. var tan = Math.atan(height / width),
  126. angle = tan * 180 / Math.PI;
  127. return tan > 0 ? (line1[0].x > line1[1].x ? angle : angle + 180) : (line1[0].x > line1[1].x ? angle + 360 : angle + 180);
  128. }
  129. },
  130. /**
  131. * 计算直线的长度
  132. * @param {*} line
  133. */
  134. lineDistance(line) {
  135. return Math.sqrt(Math.pow(line[0].x - line[1].x, 2) + Math.pow(line[0].y - line[1].y, 2))
  136. },
  137. isInfinity(num) {
  138. return num === Infinity || num === -Infinity
  139. },
  140. /**
  141. * 获取一组坐标的XY坐标区间
  142. * @param {*} points
  143. */
  144. getSection(points) {
  145. var ret = {
  146. minx: points[0].x,
  147. maxx: points[0].x,
  148. miny: points[0].y,
  149. maxy: points[0].y
  150. }
  151. points.forEach(function (point) {
  152. if (ret.minx > point.x) {
  153. ret.minx = point.x
  154. }
  155. if (ret.maxx < point.x) {
  156. ret.maxx = point.x
  157. }
  158. if (ret.miny > point.y) {
  159. ret.miny = point.y
  160. }
  161. if (ret.maxy < point.y) {
  162. ret.maxy = point.y
  163. }
  164. })
  165. return ret
  166. },
  167. /**
  168. * 计算垂直向量
  169. * @param {向量} arr
  170. */
  171. verticalLine(line) {
  172. var arr = [
  173. line.points[1].x - line.points[0].x,
  174. line.points[1].y - line.points[0].y
  175. ]
  176. if (arr.length !== 0) {
  177. var x, y;
  178. y = Math.sqrt(1 / ((arr[1] * arr[1]) / (arr[0] * arr[0]) + 1));
  179. x = Math.sqrt(1 - y * y);
  180. return [x, y];
  181. }
  182. return [];
  183. },
  184. /**
  185. * 将所有坐标转化为指定范围之间的值,但是保持比例不变
  186. * @param {*} points 要转化的坐标
  187. * @param {*} section 指定范围
  188. * @returns Array 转化后的值
  189. */
  190. tranProp(points, section) {
  191. var args = util.getSection(points)
  192. var xlen = args.maxx - args.minx
  193. var ylen = args.maxy - args.miny
  194. var prop = xlen > ylen ? xlen : ylen
  195. var offsetX = -(args.minx + xlen / 2)
  196. var offsetY = -(args.miny + ylen / 2)
  197. var range = section[1] - section[0]
  198. return {
  199. offset: {
  200. x: offsetX,
  201. y: offsetY
  202. },
  203. prop,
  204. range: range
  205. }
  206. },
  207. /**
  208. * 将屏幕转化为真实坐标
  209. * @param {*} point
  210. * @param {*} section
  211. */
  212. tranPoint(point, section) {
  213. return {
  214. x: point.x + section.minx,
  215. y: point.y + section.miny
  216. }
  217. },
  218. /**
  219. * 判断点是否在线上
  220. * @param {*} point 要判断的点
  221. * @param {*} line 线段
  222. * @param {*} width 线宽
  223. */
  224. isContainPoint(point, line, width) {
  225. var dis = 0
  226. var s1 = line[1].x - line[0].x
  227. var s2 = point.x - line[0].x
  228. var s3 = point.x - line[1].x
  229. var k1 = line[1].y - line[0].y
  230. var k2 = point.y - line[0].y
  231. var k3 = point.y - line[1].y
  232. var max = width / 2
  233. var min = 0 - max
  234. var cross = s1 * s2 + k1 * k2;
  235. var d2 = s1 * s1 + k1 * k1;
  236. if (cross <= 0) {
  237. dis = Math.sqrt(s2 * s2 + k2 * k2);
  238. } else if (cross >= d2) {
  239. dis = Math.sqrt(s3 * s3 + k3 * k3);
  240. } else {
  241. var r = cross / d2;
  242. var px = line[0].x + s1 * r;
  243. var py = line[0].y + k1 * r;
  244. dis = Math.sqrt((point.x - px) * (point.x - px) + (py - point.y) * (py - point.y));
  245. }
  246. return dis >= min && dis <= max
  247. },
  248. /**
  249. * 判断两条线段是否相交
  250. * @param {*} line1
  251. * @param {*} line2
  252. */
  253. isLineIntersect(line1, line2) {
  254. var a1 = line1[1].y - line1[0].y;
  255. var b1 = line1[0].x - line1[1].x;
  256. var c1 = a1 * line1[0].x + b1 * line1[0].y;
  257. //转换成一般式: Ax+By = C
  258. var a2 = line2[1].y - line2[0].y;
  259. var b2 = line2[0].x - line2[1].x;
  260. var c2 = a2 * line2[0].x + b2 * line2[0].y;
  261. // 计算交点
  262. var d = a1 * b2 - a2 * b1;
  263. // 当d==0时,两线平行
  264. if (d == 0) {
  265. return false;
  266. } else {
  267. var x = (b2 * c1 - b1 * c2) / d;
  268. var y = (a1 * c2 - a2 * c1) / d;
  269. // 检测交点是否在两条线段上
  270. if ((isInBetween(line1[0].x, x, line1[1].x) || isInBetween(line1[0].y, y, line1[1].y)) &&
  271. (isInBetween(line2[0].x, x, line2[1].x) || isInBetween(line2[0].y, y, line2[1].y))) {
  272. return true;
  273. }
  274. }
  275. function isInBetween(a, b, c) {
  276. // 如果b几乎等于a或c,返回false.为了避免浮点运行时两值几乎相等,但存在相差0.00000...0001的这种情况出现使用下面方式进行避免
  277. if (Math.abs(a - b) < 0.000001 || Math.abs(b - c) < 0.000001) {
  278. return false;
  279. }
  280. return (a <= b && b <= c) || (c <= b && b <= a);
  281. }
  282. return false;
  283. },
  284. /**
  285. * 判断两个点是否相同
  286. * @param {*} point1
  287. * @param {*} poin2
  288. */
  289. equalPoint(point1, poin2) {
  290. return point1.x === poin2.x && point1.y === poin2.y
  291. },
  292. /**
  293. * 判断两个面是否相交,
  294. * @param {*} face1
  295. * @param {*} face2
  296. */
  297. isFaceIntersect(face1, face2) {
  298. for (var i = 0; i < face1.length; i++) {
  299. var next = i + 1 === face1.length ? 0 : i + 1
  300. var line1 = [face1[i], face1[next]]
  301. for (var j = 0; j < face2.length; j++) {
  302. var next = j + 1 === face2.length ? 0 : j + 1
  303. var line2 = [face2[j], face2[next]]
  304. var isIntersect1 = util.isLineIntersect(line2, line1)
  305. var isIntersect2 = util.isLineIntersect(line1, line2)
  306. if (isIntersect1 && isIntersect2) {
  307. return true
  308. }
  309. }
  310. }
  311. },
  312. /**
  313. * 判断一个点是否在面上
  314. * @param {*} face1
  315. * @param {*} face2
  316. */
  317. pointInside(point, face) {
  318. var inside = false;
  319. var x = point.x,
  320. y = point.y;
  321. for (var i = 0, j = face.length - 1; i < face.length; j = i++) {
  322. var xi = face[i].x,
  323. yi = face[i].y;
  324. var xj = face[j].x,
  325. yj = face[j].y;
  326. if (((yi > y) != (yj > y)) &&
  327. (x < (xj - xi) * (y - yi) / (yj - yi) + xi)) {
  328. inside = !inside;
  329. }
  330. }
  331. return inside;
  332. }
  333. }
  334. /**
  335. * 获取一个元素在指定父元素或文档中的位置
  336. */
  337. function getPosition(dom, parent) {
  338. var ret = {
  339. x: 0,
  340. y: 0
  341. }
  342. while (dom && dom !== parent && dom !== document.documentElement) {
  343. ret.x += dom.offsetLeft
  344. ret.y += dom.offsetTop
  345. dom = dom.offsetParent
  346. }
  347. return ret
  348. }
  349. /**
  350. * 添加拖拽事件
  351. */
  352. function addMouseEvent(dom, event) {
  353. dom.addEventListener('mousedown', function downHandle(ev) {
  354. var prevPoint = {
  355. x: ev.pageX,
  356. y: ev.pageY
  357. }
  358. var con = event.down(prevPoint)
  359. if (!con) return;
  360. document.documentElement.addEventListener('mousemove', moveHandle, {
  361. passive: false
  362. })
  363. document.documentElement.addEventListener('mouseup', endHandle, {
  364. passive: false
  365. })
  366. function moveHandle(ev) {
  367. event.move({
  368. x: ev.pageX,
  369. y: ev.pageY
  370. }, prevPoint)
  371. prevPoint = {
  372. x: ev.pageX,
  373. y: ev.pageY
  374. }
  375. }
  376. function endHandle(ev) {
  377. document.documentElement.removeEventListener('mousemove', moveHandle, {
  378. passive: false
  379. })
  380. document.documentElement.removeEventListener('mouseup', endHandle, {
  381. passive: false
  382. })
  383. event.up()
  384. }
  385. }, false)
  386. }
  387. /**
  388. * 包含对象
  389. */
  390. function inherit(origin, target) {
  391. for (var key in target) {
  392. origin.prototype[key] = target[key]
  393. }
  394. }
  395. /**
  396. * 墙面跟相机
  397. */
  398. var Wall = (function Wall() {
  399. /**
  400. * 获取面的所有点
  401. * @param {*} face
  402. */
  403. function getFacePoints(face) {
  404. var start = face[0]
  405. var ret = []
  406. var iteration = start
  407. do {
  408. ret.push(iteration.points[0])
  409. iteration = face.find(function (item) {
  410. return item.id === iteration.linkedLineID[1]
  411. })
  412. } while (start !== iteration)
  413. return ret
  414. }
  415. function Wall(data, cameras) {
  416. this.lineWidth = 8
  417. this.cameras = cameras
  418. this.stateStack = []
  419. var faces = {}
  420. data.forEach(function (line) {
  421. if (faces[line.roomIndex]) {
  422. faces[line.roomIndex].push(line)
  423. } else {
  424. faces[line.roomIndex] = [line]
  425. }
  426. })
  427. this.faces = faces
  428. // this.regEvent()
  429. // this.init()
  430. // this.draw()
  431. }
  432. Wall.prototype._down = function (point, renderer) {
  433. this.preservationState()
  434. for (var key in this.faces) {
  435. var face = this.faces[key]
  436. for (var i = 0, line; line = face[i]; i++) {
  437. if (util.isContainPoint(point, line.points, (this.lineWidth + 4) / renderer.status.prop)) {
  438. return {
  439. face: face,
  440. line: line,
  441. dire: util.verticalLine(line)
  442. }
  443. }
  444. }
  445. }
  446. }
  447. Wall.prototype.updateLine = function (line, x, y) {
  448. if (!x && !y) return;
  449. var face
  450. for (var key in this.faces) {
  451. var index = this.faces[key].findIndex(function (item) {
  452. return item === line
  453. })
  454. if (~index) {
  455. face = this.faces[key]
  456. break
  457. }
  458. }
  459. if (!face) return
  460. var prevLine = face.find(function (cLine) {
  461. return line.linkedLineID[0] === cLine.id
  462. })
  463. var lastLine = face.find(function (cLine) {
  464. return line.linkedLineID[1] === cLine.id
  465. })
  466. var prevK = (prevLine.points[1].y - prevLine.points[0].y) / (prevLine.points[1].x - prevLine.points[0].x)
  467. var lastK = (lastLine.points[1].y - lastLine.points[0].y) / (lastLine.points[1].x - lastLine.points[0].x)
  468. var prevX, prevY, lastX, lastY
  469. if (x) {
  470. prevX = prevLine.points[1].x + x
  471. prevY = (prevX - prevLine.points[0].x) * prevK + prevLine.points[0].y
  472. lastX = lastLine.points[0].x + x
  473. lastY = (lastX - lastLine.points[0].x) * lastK + lastLine.points[0].y
  474. } else {
  475. prevY = prevLine.points[1].y + y
  476. prevX = (prevY - prevLine.points[0].y) / prevK + prevLine.points[0].x
  477. lastY = lastLine.points[0].y + y
  478. lastX = (lastY - lastLine.points[0].y) / lastK + lastLine.points[0].x
  479. }
  480. if (util.isInfinity(prevX) ||
  481. util.isInfinity(prevY) ||
  482. util.isInfinity(lastX) ||
  483. util.isInfinity(lastY)) {
  484. prevX = prevLine.points[1].x + x
  485. prevY = prevLine.points[1].y + y
  486. lastX = lastLine.points[0].x + x
  487. lastY = lastLine.points[0].y + y
  488. }
  489. prevLine.points[1].x = prevX
  490. prevLine.points[1].y = prevY
  491. lastLine.points[0].x = lastX
  492. lastLine.points[0].y = lastY
  493. line.points[0].x = prevLine.points[1].x
  494. line.points[0].y = prevLine.points[1].y
  495. line.points[1].x = lastLine.points[0].x
  496. line.points[1].y = lastLine.points[0].y
  497. }
  498. Wall.prototype._move = function (point, prevPoint, movePosition, renderer) {
  499. var xdis = point.x - prevPoint.x
  500. var ydis = point.y - prevPoint.y
  501. var revoke = false
  502. var dire = movePosition.dire[0] - movePosition.dire[1] > 0 ? 'X' : 'Y'
  503. if (dire === 'X') {
  504. this.updateLine(movePosition.line, xdis, 0)
  505. } else {
  506. this.updateLine(movePosition.line, 0, ydis)
  507. }
  508. var activeFacePoint = getFacePoints(movePosition.face)
  509. for (let key in this.faces) {
  510. if (this.faces[key] !== movePosition.face &&
  511. util.isFaceIntersect(activeFacePoint, getFacePoints(this.faces[key]))) {
  512. revoke = true;
  513. break;
  514. }
  515. }
  516. var cameras;
  517. if (this.cameras && (cameras = this.cameras[movePosition.face[0].roomIndex])) {
  518. for (var i = 0, camera; camera = cameras[i]; i++) {
  519. if (!util.pointInside(camera, activeFacePoint)) {
  520. revoke = true
  521. break;
  522. }
  523. }
  524. }
  525. if (revoke) {
  526. if (dire === 'X') {
  527. this.updateLine(movePosition.line, -xdis, 0)
  528. } else {
  529. this.updateLine(movePosition.line, 0, -ydis)
  530. }
  531. } else {
  532. this.trigger('changePosition', movePosition.line)
  533. renderer.render()
  534. }
  535. }
  536. Wall.prototype.draw = function (renderer, context) {
  537. for (var key in this.faces) {
  538. var face = this.faces[key]
  539. for (var i = 0, line; line = face[i]; i++) {
  540. var _line = [
  541. renderer.transScreenPoint(line.points[0]),
  542. renderer.transScreenPoint(line.points[1])
  543. ]
  544. context.beginPath();
  545. context.strokeStyle = line.selected ? 'rgb(255,85,0)' : '#fff';
  546. context.lineWidth = this.lineWidth;
  547. context.lineCap = 'round'
  548. context.moveTo(_line[0].x, _line[0].y);
  549. context.lineTo(_line[1].x, _line[1].y);
  550. context.closePath();
  551. context.stroke();
  552. }
  553. }
  554. if (this.cameras) {
  555. for (var key in this.cameras) {
  556. for (var i = 0; i < this.cameras[key].length; i++) {
  557. var point = renderer.transScreenPoint(this.cameras[key][i])
  558. context.fillStyle = "#00cc00";
  559. context.fillRect(point.x - this.lineWidth / 2, point.y - this.lineWidth / 2, this.lineWidth, this.lineWidth);
  560. }
  561. }
  562. }
  563. }
  564. Wall.prototype.preservationState = function (renderer) {
  565. var state = {}
  566. for (var key in this.faces) {
  567. state[key] = {}
  568. for (var i = 0; i < this.faces[key].length; i++) {
  569. var line = this.faces[key][i]
  570. state[key][line.id] = [{
  571. x: line.points[0].x,
  572. y: line.points[0].y
  573. },
  574. {
  575. x: line.points[1].x,
  576. y: line.points[1].y
  577. }
  578. ]
  579. }
  580. }
  581. this.stateStack.push(state)
  582. }
  583. Wall.prototype.revokeState = function (renderer) {
  584. var state = this.stateStack.pop()
  585. for (var key in state) {
  586. for (var id in state[key]) {
  587. var cline = state[key][id]
  588. var line = this.faces[key].find(function (line) {
  589. return line.id === id
  590. })
  591. line.points[0].x = cline[0].x
  592. line.points[0].y = cline[0].y
  593. line.points[1].x = cline[1].x
  594. line.points[1].y = cline[1].y
  595. }
  596. }
  597. renderer.render();
  598. }
  599. Wall.prototype.getIndex = function() {
  600. return -1
  601. }
  602. inherit(Wall, Event.create('__wall__'))
  603. return Wall
  604. })();
  605. /**
  606. * 当前所在位置
  607. */
  608. var Position = (function Position() {
  609. function Position(point, angle) {
  610. this.point = point
  611. this.angle = angle
  612. this.withinW = 8
  613. this.abroadW = 4
  614. this.lightW = 14
  615. this.lightrRdian = 1 / 2.5 * Math.PI
  616. }
  617. Position.prototype.draw = function (renderer, context) {
  618. var point = renderer.transScreenPoint(this.point)
  619. var startRdian = this.angle - this.lightrRdian / 2
  620. var endRdian = this.angle + this.lightrRdian / 2
  621. var ligWidth = this.withinW + this.abroadW + this.lightW
  622. var abroadWidth = this.withinW + this.abroadW
  623. var grd = context.createRadialGradient(point.x, point.y, abroadWidth, point.x, point.y, ligWidth);
  624. grd.addColorStop(0, "rgba(0,200,175,0.9)");
  625. grd.addColorStop(1, "rgba(0,200,175,0.3)");
  626. context.beginPath();
  627. context.fillStyle = grd;
  628. context.moveTo(point.x, point.y);
  629. context.arc(point.x, point.y, ligWidth, startRdian, endRdian, false);
  630. context.fill()
  631. context.closePath();
  632. context.beginPath();
  633. context.fillStyle = "#ffffff";
  634. context.arc(point.x, point.y, abroadWidth, 0, 2 * Math.PI, false);
  635. context.fill()
  636. context.closePath();
  637. context.beginPath();
  638. context.fillStyle = "rgba(0,200,175,1)";
  639. context.arc(point.x, point.y, this.withinW, 0, 2 * Math.PI, false);
  640. context.fill()
  641. context.closePath();
  642. }
  643. inherit(Position, Event.create('__position__'))
  644. return Position
  645. })();
  646. /**
  647. * 门(第一个版本)
  648. */
  649. (function Door() {
  650. function Door(line, point) {
  651. this.line = line
  652. this.point = point
  653. this.lineWidth = 8
  654. this.color = 'rgba(0,200,175,1)'
  655. }
  656. Door.prototype.getPoints = function () {
  657. var cpoint = util.lineVector(this.line)
  658. var vpoint = util.verticalLine(this.line)
  659. var width = 3
  660. var point1 = {
  661. x: this.point.x + cpoint.x * width,
  662. y: this.point.y + cpoint.y * width
  663. }
  664. var point2 = {
  665. x: this.point.x + vpoint[0] * width,
  666. y: this.point.y + vpoint[1] * width
  667. }
  668. return [this.point, point1, point2]
  669. }
  670. Door.prototype.draw = function (renderer, context) {
  671. points = this.getPoints().map(function (point) {
  672. return renderer.transScreenPoint(point)
  673. })
  674. var startDeg = util.lineAngle([points[1], points[0]]) * Math.PI / 180
  675. var endDeg = util.lineAngle([points[2], points[0]]) * Math.PI / 180
  676. var deg = endDeg - startDeg
  677. var router = ((deg > Math.PI || (deg < 0 && deg > -Math.PI)) ? true : false)
  678. var r = util.lineDistance([points[1], points[0]])
  679. var grd = context.createLinearGradient(points[0].x, points[0].y, points[0].x, points[0].y - 100);
  680. grd.addColorStop(0, "rgba(25,31,31,1)");
  681. grd.addColorStop(0.3, "rgba(255,255,255,1)");
  682. grd.addColorStop(0.7, "rgba(255,255,255,1)");
  683. grd.addColorStop(0.71, "rgba(25,31,31,1)");
  684. context.strokeStyle = this.color;
  685. context.lineWidth = this.lineWidth;
  686. context.beginPath();
  687. context.moveTo(points[0].x, points[0].y);
  688. context.arc(points[0].x, points[0].y, r, startDeg, endDeg, router);
  689. context.lineTo(points[0].x, points[0].y);
  690. context.stroke()
  691. context.closePath();
  692. context.beginPath();
  693. context.strokeStyle = '#000';
  694. context.moveTo(points[0].x, points[0].y);
  695. context.lineTo(points[1].x, points[1].y);
  696. context.stroke()
  697. context.closePath();
  698. }
  699. return Door
  700. });
  701. /**
  702. * 门,正式版本
  703. */
  704. var Door = (function Door() {
  705. var count = 0
  706. function Door(line, points) {
  707. count++
  708. this.line = line
  709. this.points = points
  710. this.lineWidth = 8
  711. this.color = 'rgba(0,200,175,1)'
  712. }
  713. Door.prototype.getIndex = function() {
  714. return 1;
  715. }
  716. Door.prototype.detection = function () {
  717. var collision = true
  718. var detection = []
  719. this.line.doors && detection.push.apply(detection, this.line.doors)
  720. this.line.casements && detection.push.apply(detection, this.line.casements)
  721. for (var i = 0; i < detection.length; i++) {
  722. if (detection[i].points !== this.points && (
  723. util.isContainPoint(this.points[0], detection[i].points, 0.1) ||
  724. util.isContainPoint(this.points[1], detection[i].points, 0.1)
  725. )) {
  726. collision = false
  727. }
  728. }
  729. return collision && util.isContainPoint(this.points[0], this.line.points, 0.1) &&
  730. util.isContainPoint(this.points[1], this.line.points, 0.1)
  731. }
  732. Door.prototype._down = function (point) {
  733. var info = {
  734. vector: util.lineVector(this.line)
  735. };
  736. if (util.lineDistance([point, this.points[0]]) < 0.3) {
  737. info.scalePoint = 0
  738. } else if (util.lineDistance([point, this.points[1]]) < 0.3) {
  739. info.scalePoint = 1
  740. } else if (!util.isContainPoint(point, this.points, 0.5)) {
  741. info = null
  742. }
  743. return info
  744. }
  745. Door.prototype.getMoveChange = function (vector, curr, prev) {
  746. var move = {
  747. x: vector.x < 0 ? (prev.x - curr.x) : (curr.x - prev.x),
  748. y: vector.y < 0 ? (prev.y - curr.y) : (curr.y - prev.y)
  749. }
  750. if (Math.abs(vector.x) > Math.abs(vector.y)) {
  751. move = {
  752. x: move.x * vector.x,
  753. y: move.x * vector.y
  754. }
  755. } else {
  756. move = {
  757. x: move.y * vector.x,
  758. y: move.y * vector.y
  759. }
  760. }
  761. return move
  762. }
  763. Door.prototype._move = function (point, prevPoint, info, renderer) {
  764. if (info.scalePoint !== undefined) {
  765. this.scale(point, prevPoint, info, renderer)
  766. } else {
  767. this.move(point, prevPoint, info.vector, renderer)
  768. }
  769. }
  770. Door.prototype.move = function (point, prevPoint, vector, renderer) {
  771. var move = this.getMoveChange(vector, point, prevPoint)
  772. this.points.forEach(function (point) {
  773. point.x += move.x
  774. point.y += move.y
  775. })
  776. if (this.detection()) {
  777. renderer.render()
  778. this.trigger('changePoints')
  779. } else {
  780. this.points.forEach(function (point) {
  781. point.x -= move.x
  782. point.y -= move.y
  783. })
  784. }
  785. }
  786. Door.prototype.scale = function (point, prevPoint, info, renderer) {
  787. var move = this.getMoveChange(info.vector, point, prevPoint)
  788. this.points[info.scalePoint].x += move.x
  789. this.points[info.scalePoint].y += move.y
  790. if (this.detection()) {
  791. renderer.render()
  792. this.trigger('changePoints')
  793. } else {
  794. this.points[info.scalePoint].x -= move.x
  795. this.points[info.scalePoint].y -= move.y
  796. }
  797. }
  798. Door.prototype.draw = function (renderer, context) {
  799. var points = this.points.map(function (point) {
  800. return renderer.transScreenPoint(point)
  801. })
  802. context.strokeStyle = this.color;
  803. context.lineWidth = this.lineWidth;
  804. context.beginPath();
  805. context.moveTo(points[0].x, points[0].y);
  806. context.lineTo(points[1].x, points[1].y);
  807. context.stroke()
  808. context.closePath();
  809. }
  810. inherit(Door, Event.create('__door' + count + '__'))
  811. return Door
  812. })();
  813. /**
  814. * 窗
  815. */
  816. var Casement = (function Casement() {
  817. var count = 0
  818. function Casement(line, points) {
  819. count++
  820. this.line = line
  821. this.points = points
  822. this.lineWidth = 8
  823. this.color = '#202123'
  824. }
  825. Casement.prototype.getIndex = function () {
  826. return 1;
  827. }
  828. Casement.prototype.detection = function () {
  829. var collision = true
  830. var detection = []
  831. this.line.doors && detection.push.apply(detection, this.line.doors)
  832. this.line.casements && detection.push.apply(detection, this.line.casements)
  833. for (var i = 0; i < detection.length; i++) {
  834. if (detection[i].points !== this.points &&(
  835. util.isContainPoint(this.points[0], detection[i].points, 0.1) ||
  836. util.isContainPoint(this.points[1], detection[i].points, 0.1)
  837. )
  838. ) {
  839. collision = false
  840. }
  841. }
  842. return collision && util.isContainPoint(this.points[0], this.line.points, 0.1) &&
  843. util.isContainPoint(this.points[1], this.line.points, 0.1)
  844. }
  845. Casement.prototype._down = function (point) {
  846. var info = {
  847. vector: util.lineVector(this.line)
  848. };
  849. if (util.lineDistance([point, this.points[0]]) < 0.3) {
  850. info.scalePoint = 0
  851. } else if (util.lineDistance([point, this.points[1]]) < 0.3) {
  852. info.scalePoint = 1
  853. } else if (!util.isContainPoint(point, this.points, 0.5)) {
  854. info = null
  855. }
  856. return info
  857. }
  858. Casement.prototype.getMoveChange = function (vector, curr, prev) {
  859. var move = {
  860. x: vector.x < 0 ? (prev.x - curr.x) : (curr.x - prev.x),
  861. y: vector.y < 0 ? (prev.y - curr.y) : (curr.y - prev.y)
  862. }
  863. if (Math.abs(vector.x) > Math.abs(vector.y)) {
  864. move = {
  865. x: move.x * vector.x,
  866. y: move.x * vector.y
  867. }
  868. } else {
  869. move = {
  870. x: move.y * vector.x,
  871. y: move.y * vector.y
  872. }
  873. }
  874. return move
  875. }
  876. Casement.prototype._move = function (point, prevPoint, info, renderer) {
  877. if (info.scalePoint !== undefined) {
  878. this.scale(point, prevPoint, info, renderer)
  879. } else {
  880. this.move(point, prevPoint, info.vector, renderer)
  881. }
  882. }
  883. Casement.prototype.move = function (point, prevPoint, vector, renderer) {
  884. var move = this.getMoveChange(vector, point, prevPoint)
  885. this.points.forEach(function (point) {
  886. point.x += move.x
  887. point.y += move.y
  888. })
  889. if (this.detection()) {
  890. renderer.render()
  891. this.trigger('changePoints')
  892. } else {
  893. this.points.forEach(function (point) {
  894. point.x -= move.x
  895. point.y -= move.y
  896. })
  897. }
  898. }
  899. Casement.prototype.scale = function (point, prevPoint, info, renderer) {
  900. var move = this.getMoveChange(info.vector, point, prevPoint)
  901. this.points[info.scalePoint].x += move.x
  902. this.points[info.scalePoint].y += move.y
  903. if (this.detection()) {
  904. renderer.render()
  905. this.trigger('changePoints')
  906. } else {
  907. this.points[info.scalePoint].x -= move.x
  908. this.points[info.scalePoint].y -= move.y
  909. }
  910. }
  911. Casement.prototype.draw = function (renderer, context) {
  912. var points = this.points.map(function (point) {
  913. return renderer.transScreenPoint(point)
  914. })
  915. context.strokeStyle = this.color;
  916. context.lineWidth = this.lineWidth;
  917. context.beginPath();
  918. context.moveTo(points[0].x, points[0].y);
  919. context.lineTo(points[1].x, points[1].y);
  920. context.stroke()
  921. context.closePath();
  922. }
  923. inherit(Casement, Event.create('__casement' + count + '__'))
  924. return Casement
  925. })();
  926. /**
  927. * 总控制
  928. */
  929. var Renderer = (function Renderer() {
  930. var execFilter = 'FUNCTION_EXEC'
  931. /**
  932. * 渲染器
  933. * @param {*} canvas 画布
  934. * @param {*} wallData 墙面数据
  935. */
  936. function Renderer(canvas, wallData) {
  937. this.canvas = canvas
  938. this.context = canvas.getContext('2d')
  939. this.origin = wallData
  940. this.models = []
  941. this.init()
  942. this.regEvent()
  943. }
  944. /**
  945. * 计算所有需要的信息,渲染器需要的宽高,真实比例等
  946. */
  947. Renderer.prototype.init = function () {
  948. this.width = this.canvas.width
  949. this.height = this.canvas.height
  950. this.wMultiple = this.canvas.width / this.canvas.offsetWidth
  951. this.hMultiple = this.canvas.height / this.canvas.offsetHeight
  952. this.context.translate(this.width / 2, this.height / 2);
  953. this.screenOffset = getPosition(this.canvas)
  954. var points = []
  955. this.origin.forEach(function (line) {
  956. points = points.concat(...line.points)
  957. })
  958. var result = util.tranProp(points, [-0.99, 0.99])
  959. this.status = {
  960. offset: result.offset,
  961. prop: result.prop,
  962. range: result.range
  963. }
  964. }
  965. /**
  966. * 真实点位转化为画布点位
  967. */
  968. Renderer.prototype.transScreenPoint = function (point) {
  969. var spoint = {
  970. x: point.x,
  971. y: point.y
  972. }
  973. spoint.x += this.status.offset.x
  974. spoint.y += this.status.offset.y
  975. spoint.x = (spoint.x * this.status.range / this.status.prop) * (this.width / 2)
  976. spoint.y = (spoint.y * this.status.range / this.status.prop) * (this.height / 2)
  977. return spoint
  978. }
  979. /**
  980. * 画布点位转化为真实点位
  981. */
  982. Renderer.prototype.transRealPoint = function (point) {
  983. return {
  984. x: (point.x * this.status.prop) / (this.status.range * this.width / 2) - this.status.offset.x,
  985. y: (point.y * this.status.prop) / (this.status.range * this.height / 2) - this.status.offset.y
  986. }
  987. }
  988. Renderer.prototype.regEvent = function () {
  989. var sinfo, model;
  990. var section = {
  991. minx: -this.width / 2,
  992. maxx: this.width / 2,
  993. miny: -this.height / 2,
  994. maxy: this.height / 2
  995. }
  996. var startPoint = null
  997. var prevPoint = null
  998. var event = {
  999. down(offset) {
  1000. offset.x = (offset.x - this.screenOffset.x) * this.wMultiple
  1001. offset.y = (offset.y - this.screenOffset.y) * this.hMultiple
  1002. startPoint = this.transRealPoint(util.tranPoint(offset, section))
  1003. prevPoint = startPoint
  1004. var models = Object.assign([], this.models).sort(function(a, b) {
  1005. return ((b.getIndex && b.getIndex()) || 0) - ((a.getIndex && a.getIndex()) || 0)
  1006. })
  1007. for (var i = 0; i < models.length; i++) {
  1008. sinfo = models[i]._down && models[i]._down(startPoint, this)
  1009. if (sinfo) {
  1010. model = models[i]
  1011. break;
  1012. }
  1013. }
  1014. return sinfo
  1015. },
  1016. move(move) {
  1017. move.x = (move.x - this.screenOffset.x) * this.wMultiple
  1018. move.y = (move.y - this.screenOffset.y) * this.hMultiple
  1019. var point = this.transRealPoint(util.tranPoint(move, section))
  1020. model._move(point, prevPoint, sinfo, this, this.context)
  1021. prevPoint = point
  1022. },
  1023. up() {
  1024. model._up && model._up(startPoint, prevPoint, this, this.context)
  1025. startPoint = null
  1026. prevPoint = null
  1027. model = null
  1028. sinfo = null
  1029. }
  1030. }
  1031. event.down = event.down.bind(this)
  1032. event.move = event.move.bind(this)
  1033. event.up = event.up.bind(this)
  1034. addMouseEvent(this.canvas, event)
  1035. }
  1036. /**
  1037. * 统一执行命令
  1038. */
  1039. Renderer.prototype.unifiedExec = function () {
  1040. var args = Array.from(arguments)
  1041. var command = args.shift()
  1042. var ret = []
  1043. var filter = args[args.length - 1] === execFilter
  1044. this.models.map(function (model) {
  1045. if (model[command]) {
  1046. if (filter) {
  1047. ret.push(args[0].apply(model, [model, args.slice(1, args.length - 1)]))
  1048. } else {
  1049. ret.push(model[command].apply(model, args))
  1050. }
  1051. } else {
  1052. ret.push(null)
  1053. }
  1054. })
  1055. return ret
  1056. }
  1057. /**
  1058. * 保存状态
  1059. */
  1060. Renderer.prototype.preservationState = function () {
  1061. this.unifiedExec('preservationState', this)
  1062. }
  1063. Renderer.prototype.revokeState = function () {
  1064. this.unifiedExec('revokeState', this)
  1065. }
  1066. /**
  1067. * 渲染所有依赖了渲染器的模型
  1068. */
  1069. Renderer.prototype.render = function () {
  1070. var that = this
  1071. that.context.clearRect(-that.width / 2, -that.height / 2, that.width, that.height)
  1072. that.unifiedExec('draw', function (model) {
  1073. that.context.save()
  1074. model.draw(that, that.context)
  1075. that.context.restore()
  1076. }, execFilter);
  1077. }
  1078. return Renderer
  1079. })();
  1080. function update3D(line) {
  1081. if (line.updateLine3D) {
  1082. line.updateLine3D()
  1083. } else {
  1084. console.error('当前线条没有注册3D线条方法')
  1085. }
  1086. }
  1087. function update2d(wall, line) {
  1088. var update = line.update
  1089. line.update = function (dirs, len) {
  1090. var args = [
  1091. {x: 0, y: 0},
  1092. {x: 0, y: 0},
  1093. {
  1094. line: this,
  1095. face: wall.faces[this.roomIndex]
  1096. }
  1097. ]
  1098. args[0][dirs[0]] = len;
  1099. wall._move.apply(wall, args.concat([dirs[1]]))
  1100. update && update.bind(this)()
  1101. }
  1102. }
  1103. return function (dom, line2Ds, cameras) {
  1104. var doors = []
  1105. var casements = []
  1106. var wall = new Wall(line2Ds, cameras)
  1107. line2Ds.forEach(function(line) {
  1108. if (line.doors) {
  1109. doors.push.apply(doors,
  1110. line.doors.map(function(door) {
  1111. return new Door(line, door.points)
  1112. })
  1113. )
  1114. }
  1115. if (line.casements) {
  1116. casements.push.apply(casements,
  1117. line.casements.map(function (casements) {
  1118. return new Casement(line, casements.points)
  1119. })
  1120. )
  1121. }
  1122. update2d(wall, line)
  1123. })
  1124. wall.listen('changePosition', function(line2D) {
  1125. line2Ds.forEach(function (line) {
  1126. if (line.id === line2D.linkedLineID[0] ||
  1127. line.id === line2D.linkedLineID[1]) {
  1128. update3D(line)
  1129. }
  1130. })
  1131. update3D(line2D)
  1132. })
  1133. doors.concat(casements).forEach(function (obj) {
  1134. obj.listen('changePoints', function() {
  1135. update3D(obj.line)
  1136. })
  1137. })
  1138. var renderer = new Renderer(dom, line2Ds)
  1139. renderer.models.push(wall)
  1140. renderer.models.push.apply(renderer.models, doors.concat(casements))
  1141. renderer.render()
  1142. var position;
  1143. return {
  1144. position: function (posPoint, angle) {
  1145. if (!position) {
  1146. position = new Position(posPoint, angle)
  1147. renderer.models.push(position)
  1148. } else {
  1149. position.point = posPoint
  1150. position.angle = angle
  1151. }
  1152. renderer.render()
  1153. },
  1154. renderer: renderer
  1155. }
  1156. }
  1157. })();