bk.ts 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553
  1. import Render from '../renderer'
  2. import Point from '../../core/point'
  3. import WallLine from '../../core/wallline'
  4. import Casement from '../../architecture/casement'
  5. import Door from '../../architecture/door/index'
  6. import Column from '../../architecture/column'
  7. import { CADElementTS } from '../../core/element'
  8. import { ProcessingTS } from '.'
  9. export interface _Point { x: number, y:number, id: number }
  10. export interface _Line { p1: number, p2: number, id: number }
  11. export interface _window { pos: Array<number>, line: number, top: number, bottom: number }
  12. export interface _door { pos: Array<number>, line: number, top: number, bottom: number }
  13. export interface _column { pos: Array<number>, line: number }
  14. export interface _hole {pos: Array<number>, top: number}
  15. export type _Ground = Array<number>
  16. export interface _room { ground: _Ground, hole: Array<_hole>, top: number, bottom: number }
  17. export interface Data {vertex: Array<_Point>, surplus: Array<_Point>, wall: Array<_Line>, window: Array<_window>, door: Array<_door>, column: Array<_column>, room: Array<_room>}
  18. export interface StorageProps{ dom: HTMLElement }
  19. export interface CADState {elements: Array<Point>}
  20. export interface rhole extends _hole {bottom: number}
  21. type Walls = Array<{ id: number; ele: WallLine }>
  22. type Points = Array<{ id: number; ele: Point }>
  23. type Cases = Array<{ele: Casement}>
  24. type Doors = Array<{ele: Door}>
  25. type Columns = Array<{ele: Column}>
  26. const numRetain = (n: number, m = 2) => Number(n.toFixed(m))
  27. class Processing {
  28. data: Data
  29. render: Render
  30. points: Points
  31. lines: Walls
  32. cases: Cases
  33. doors: Doors
  34. columns: Columns
  35. constructor({dom}: StorageProps) {
  36. this.render = new Render({ layer: dom, processing: (this as unknown as ProcessingTS) })
  37. this.points = []
  38. this.lines = []
  39. this.cases = []
  40. this.doors = []
  41. this.columns = []
  42. WallLine.initLines(this.render)
  43. }
  44. getNewPointId = () => Math.max(...this.points.map(({id}) => id)) + 1
  45. getNewLineId = () => Math.max(...this.lines.map(({id}) => id)) + 1
  46. addPoint = ({id, x, y}: _Point) => {
  47. let ret = {id, ele: new Point({x, y, renderer: this.render})}
  48. this.points.push(ret)
  49. this.generateElement(ret.ele)
  50. return ret
  51. }
  52. addLine = ({id, p1, p2}: _Line) => {
  53. const rooms = this.data.room
  54. const isOut = rooms.some(({hole}) => hole.some(({pos: h}) => ~h.indexOf(p1) && ~h.indexOf(p2)))
  55. let ret = {
  56. id,
  57. ele: new WallLine({
  58. points: [
  59. this.points.find(p => p.id === p1).ele,
  60. this.points.find(p => p.id === p2).ele
  61. ],
  62. renderer: this.render,
  63. color: isOut ? 'red': void 0,
  64. isOut
  65. })
  66. }
  67. this.lines.push(ret)
  68. this.generateElement(ret.ele)
  69. return ret
  70. }
  71. addCase = ({pos, line, top = null, bottom = null}: _window) => {
  72. let ret = {
  73. ele: new Casement({
  74. renderer: this.render,
  75. attachment: this.lines.find(({id}) => id === line).ele,
  76. points: [{x: pos[0], y: pos[1]}, {x: pos[2], y: pos[3]}],
  77. top,
  78. bottom
  79. })
  80. }
  81. this.cases.push(ret)
  82. this.generateElement(ret.ele)
  83. return ret
  84. }
  85. addDoor = ({pos, line, top = null, bottom = null}: _door) => {
  86. let ret = {
  87. ele: new Door({
  88. renderer: this.render,
  89. attachment: this.lines.find(({id}) => id === line).ele,
  90. points: [{x: pos[0], y: pos[1]}, {x: pos[2], y: pos[3]}],
  91. top, bottom
  92. })
  93. }
  94. this.doors.push(ret)
  95. this.generateElement(ret.ele)
  96. return ret
  97. }
  98. addColumn = ({pos, line}: _column) => {
  99. let ret = {
  100. ele: new Column({
  101. renderer: this.render,
  102. attachment: this.lines.find(({ id }) => id === line).ele,
  103. points: [
  104. {x: pos[0], y: pos[1]},
  105. {x: pos[2], y: pos[3]},
  106. {x: pos[6], y: pos[7]},
  107. {x: pos[4], y: pos[5]}
  108. ]
  109. })
  110. }
  111. this.columns.push(ret)
  112. this.generateElement(ret.ele)
  113. return ret
  114. }
  115. // 获取与当前room有关联的所有room
  116. getJoinRooms(room: _room) {
  117. const checkRooms = []
  118. const queryRooms = (room: _room) => {
  119. if (~checkRooms.indexOf(room)) {
  120. return []
  121. } else {
  122. let rooms = this.data.room.filter(({ground}) => ground.some(id => ~room.ground.indexOf(id)))
  123. checkRooms.push(room)
  124. rooms.forEach(room => rooms.push(...queryRooms(room)))
  125. return rooms
  126. }
  127. }
  128. return Array.from(new Set(queryRooms(room)))
  129. }
  130. getPointsByRoom (room: _room) {
  131. room = this.data.room.find(r => r.ground.join('') === room.ground.join(''))
  132. if (room) {
  133. return room.ground.map(eid => this.points.find(({id}) => id === eid).ele)
  134. } else {
  135. return []
  136. }
  137. }
  138. getPointsByHole (rhole: rhole) {
  139. let rhids = rhole.pos.join('')
  140. let hole
  141. for (let i = 0; i < this.data.room.length; i++) {
  142. hole = this.data.room[i].hole.find(hole => hole.pos.join('') === rhids)
  143. if(hole) break
  144. }
  145. if (hole) {
  146. return hole.pos.map(eid => this.points.find(({id}) => id === eid).ele)
  147. } else {
  148. return []
  149. }
  150. }
  151. getLinesByRoom (room: _room) {
  152. room = this.data.room.find(r => r.ground.join('') === room.ground.join(''))
  153. if (room) {
  154. return this.lines.filter(({ele: line}) => {
  155. let p1 = this.points.find(({ele}) => ele === line.points[0]).id
  156. let p2 = this.points.find(({ele}) => ele === line.points[1]).id
  157. return ~room.ground.indexOf(p1) && ~room.ground.indexOf(p2)
  158. }).map(({ele}) => ele)
  159. } else {
  160. return []
  161. }
  162. }
  163. getLinesByHole (rhole: rhole) {
  164. let rhids = rhole.pos.join('')
  165. let hole
  166. for (let i = 0; i < this.data.room.length; i++) {
  167. hole = this.data.room[i].hole.find(hole => hole.pos.join('') === rhids)
  168. if(hole) break
  169. }
  170. if (hole) {
  171. return this.lines.filter(({ele: line}) => {
  172. let p1 = this.points.find(({ele}) => ele === line.points[0]).id
  173. let p2 = this.points.find(({ele}) => ele === line.points[1]).id
  174. return ~hole.pos.indexOf(p1) && ~hole.pos.indexOf(p2)
  175. }).map(({ele}) => ele)
  176. } else {
  177. return []
  178. }
  179. }
  180. getRoomsByPoint (point: Point) {
  181. let p = this.points.find(({ele}) => ele === point)
  182. if (p) {
  183. return this.data.room.filter(room => ~room.ground.indexOf(p.id))
  184. } else {
  185. return []
  186. }
  187. }
  188. getHolesByPoint (point: Point): Array<rhole> {
  189. let p = this.points.find(({ele}) => ele === point)
  190. let holes = []
  191. if (p) {
  192. this.data.room.find(room => {
  193. room.hole.forEach(hole => {
  194. if (~hole.pos.indexOf(p.id)) {
  195. holes.push(hole)
  196. }
  197. })
  198. })
  199. } else {
  200. console.error(point)
  201. }
  202. return holes
  203. }
  204. getRoomsByLine (line: WallLine) {
  205. let p1 = this.points.find(point => point.ele === line.points[0]).id
  206. let p2 = this.points.find(point => point.ele === line.points[1]).id
  207. return this.data.room.filter(room => ~room.ground.indexOf(p1) && ~room.ground.indexOf(p2))
  208. }
  209. getHolesByLine (line: WallLine): Array<_hole & {bottom: number}> {
  210. let p1 = this.points.find(point => point.ele === line.points[0]).id
  211. let p2 = this.points.find(point => point.ele === line.points[1]).id
  212. let holes = []
  213. this.data.room.forEach(room => {
  214. room.hole.forEach(hole => {
  215. if (~hole.pos.indexOf(p1) && ~hole.pos.indexOf(p2)) {
  216. holes.push(hole)
  217. }
  218. })
  219. })
  220. return holes
  221. }
  222. getLineId = (line: WallLine) => this.lines.find(({ele}) => ele === line).id
  223. getPointId = (point: Point) => this.points.find(({ele}) => ele === point).id
  224. // 将数据转换为ele
  225. toEles (data: Data) {
  226. let {vertex, wall, window, door, column} = data
  227. this.data = data
  228. vertex.forEach(this.addPoint)
  229. wall.forEach(this.addLine)
  230. window.forEach(this.addCase)
  231. door.forEach(this.addDoor)
  232. column.forEach(this.addColumn)
  233. this.data.room.forEach(room => {
  234. room.hole.forEach(hole => {
  235. Object.defineProperty(hole, 'bottom', {
  236. get: () => room.bottom
  237. })
  238. })
  239. })
  240. this.referElements()
  241. }
  242. // 将ele转换为data
  243. toData (): Data {
  244. let vertex = this.points.map(({ele, id}) => ({
  245. id, x: numRetain(ele.x), y: numRetain(ele.y)
  246. }))
  247. let wall = this.lines.map(({ele, id}) => ({
  248. id,
  249. p1: this.getPointId(ele.points[0]),
  250. p2: this.getPointId(ele.points[1])
  251. }))
  252. let column = this.columns.map(({ele}) => ({
  253. line: this.getLineId(ele.attachment),
  254. pos: [
  255. numRetain(ele.points[0].x), numRetain(ele.points[0].y),
  256. numRetain(ele.points[1].x), numRetain(ele.points[1].y),
  257. numRetain(ele.points[3].x), numRetain(ele.points[3].y),
  258. numRetain(ele.points[2].x), numRetain(ele.points[2].y)
  259. ]
  260. }))
  261. let window = this.cases.map(({ele}) =>({
  262. line: this.getLineId(ele.attachment),
  263. pos: [
  264. numRetain(ele.linePoints[0].x), numRetain(ele.linePoints[0].y),
  265. numRetain(ele.linePoints[1].x), numRetain(ele.linePoints[1].y)
  266. ],
  267. top: ele.top,
  268. bottom: ele.bottom
  269. }))
  270. let door = this.doors.map(({ele}) =>({
  271. line: this.getLineId(ele.attachment),
  272. pos: [
  273. numRetain(ele.linePoints[0].x), numRetain(ele.linePoints[0].y),
  274. numRetain(ele.linePoints[1].x), numRetain(ele.linePoints[1].y)
  275. ],
  276. top: ele.top,
  277. bottom: ele.bottom
  278. }))
  279. return {vertex, wall, window, door, column, room: this.data.room, surplus: this.data.surplus}
  280. }
  281. referElements() {
  282. let eles = [
  283. ...this.lines,
  284. ...this.points,
  285. ...this.cases,
  286. ...this.doors,
  287. ...this.columns
  288. ]
  289. eles.forEach(({ele}) => {
  290. this.render.g.removeChild(ele.real)
  291. this.render.elements.splice(this.render.elements.indexOf(ele), 1);
  292. })
  293. this.generateElements()
  294. }
  295. // 删除房间
  296. delRoom(cground: Array<number>) {
  297. let index = this.data.room.findIndex(({ground}) => cground === ground)
  298. if (~index) {
  299. let [room] = this.data.room.splice(index, 1)
  300. room.ground.forEach((p1Id, i) => {
  301. if (i > room.ground.length - 2) return;
  302. let p2Id = room.ground[i + 1]
  303. let arr = [p1Id, p2Id]
  304. let line = this.lines.find(({ele}) => {
  305. let p1 = this.points.find(({ele: p}) => p === ele.points[0])
  306. let p2 = this.points.find(({ele: p}) => p === ele.points[1])
  307. return p1 && p2 && ~arr.indexOf(p1.id) && ~arr.indexOf(p2.id)
  308. })
  309. line && line.ele.destroy()
  310. })
  311. return true
  312. } else {
  313. return false
  314. }
  315. }
  316. // 删除镂空区域
  317. delHole(cground: Array<number>) {
  318. let hIndex = -1;
  319. let rindex = this.data.room.findIndex(({hole}) => {
  320. hIndex = hole.findIndex(({pos}) => pos === cground)
  321. return ~hIndex
  322. })
  323. if (~rindex) {
  324. let [hole] = this.data.room[rindex].hole.splice(hIndex, 1)
  325. hole.pos.forEach((p1Id, i) => {
  326. if (i > hole.pos.length - 2) return;
  327. let p2Id = hole.pos[i + 1]
  328. let arr = [p1Id, p2Id]
  329. let line = this.lines.find(({ele}) => {
  330. let p1 = this.points.find(({ele: p}) => p === ele.points[0])
  331. let p2 = this.points.find(({ele: p}) => p === ele.points[1])
  332. return p1 && p2 && ~arr.indexOf(p1.id) && ~arr.indexOf(p2.id)
  333. })
  334. line && line.ele.destroy()
  335. })
  336. return true
  337. } else {
  338. return false
  339. }
  340. }
  341. // 构建所有ele
  342. generateElements() {
  343. let eles = [
  344. ...this.lines.map(line => line.ele),
  345. ...this.points.map(point => point.ele),
  346. ...this.cases.map(c => c.ele),
  347. ...this.doors.map(c => c.ele),
  348. ...this.columns.map(c => c.ele)
  349. ]
  350. eles.forEach(this.generateElement)
  351. }
  352. attrs = [ 'cases', 'doors', 'columns', 'lines', 'points']
  353. // Element加装Destroy方法
  354. retrofitElementDestroy (ele: CADElementTS) {
  355. let destroy = ele.destroy
  356. if (ele.__load_destroy) return;
  357. ele.__load_destroy = true
  358. ele.destroy = (...args) => {
  359. // rooms字段中删掉
  360. if (!(args as any)[1] && ele instanceof WallLine) {
  361. let p1 = this.points.find(pe => pe.ele === ele.points[0]).id
  362. let p2 = this.points.find(pe => pe.ele === ele.points[1]).id
  363. this.data.room.forEach(room => {
  364. if (~room.ground.indexOf(p1) && ~room.ground.indexOf(p2)) {
  365. room.ground.splice(room.ground.indexOf(p1), 1)
  366. // 如果一个房间或者镂空区域只剩两个点则销毁
  367. if (room.ground.length <= 2) {
  368. this.delHole(room.ground)
  369. }
  370. }
  371. room.hole.forEach(hole => {
  372. if (~hole.pos.indexOf(p1) && ~hole.pos.indexOf(p2)) {
  373. hole.pos.splice(hole.pos.indexOf(p1), 1)
  374. // 如果一个房间或者镂空区域只剩两个点则销毁
  375. if (hole.pos.length <= 2) {
  376. this.delHole(hole.pos)
  377. }
  378. }
  379. })
  380. })
  381. // let rooms = this.queryLineRoomPos(ele)
  382. // for (let i = 0; i < rooms.length; i++) {
  383. // let {pos, arr} = rooms[i]
  384. // arr.splice(pos, 1)
  385. // // 如果一个房间或者镂空区域只剩两个点则销毁
  386. // if (arr.length <= 2) {
  387. // this.delHole(arr) || this.delRoom(arr)
  388. // }
  389. // }
  390. }
  391. const rep = () => {
  392. // 帮自身缓存的数组删除
  393. this.attrs.forEach(attr => {
  394. let index = this[attr].findIndex(({ele: e}) => e === ele)
  395. if (~index) {
  396. this[attr].splice(index, 1)
  397. ele.__id = this[attr][index] && this[attr][index].id
  398. }
  399. })
  400. this.render.remove(ele)
  401. }
  402. // 需要同步删除还是更新完属性后删除
  403. if ((args as any)[0]) {
  404. rep()
  405. } else {
  406. ele.nextTick(rep)
  407. }
  408. destroy.call(ele, ...args)
  409. ele.__load_destroy = false
  410. }
  411. }
  412. // Element加装Destroy方法
  413. retrofitElementIntercept (ele: WallLine) {
  414. let intercept = ele.intercept
  415. if (ele.__load_intercept) return;
  416. ele.__load_intercept = true
  417. ele.intercept = (...args) => {
  418. let isPoints = Object.keys(args[1]).indexOf('points')
  419. // 如果更改了points点要同步到rooms
  420. if (~isPoints) {
  421. let oldIds = ele.points.map(p => this.points.find(({ele}) => ele === p).id)
  422. let rooms = this.queryLineRoomPos(ele)
  423. ele.nextTick(() => {
  424. let newIds = ele.points.map(p => this.points.find(({ele}) => ele === p).id)
  425. rooms.forEach(({arr: ground}) => {
  426. let oindex1 = ground.indexOf(oldIds[0])
  427. let oindex2 = ground.indexOf(oldIds[1])
  428. ~oindex1 && (ground[oindex1] = newIds[0])
  429. ~oindex2 && (ground[oindex2] = newIds[1])
  430. })
  431. })
  432. }
  433. return intercept.apply(ele, args)
  434. }
  435. }
  436. generateElement = (ele: CADElementTS) => {
  437. this.render.push(ele)
  438. this.retrofitElementDestroy(ele)
  439. ele instanceof WallLine &&
  440. this.retrofitElementIntercept(ele)
  441. }
  442. // 查找线段第一个点在rooms中的位置
  443. queryLineRoomPos (line: WallLine) {
  444. let rooms = this.data.room
  445. let p1 = this.points.find(pe => pe.ele === line.points[0])
  446. let p2 = this.points.find(pe => pe.ele === line.points[1])
  447. let hs = []
  448. rooms = rooms.filter((room, i) => {
  449. let i1 = room.ground.indexOf(p1.id)
  450. let i2 = room.ground.indexOf(p2.id)
  451. let j = Math.abs(i1 - i2)
  452. return ~i1 && ~i2 && (j === 1 || j === room.ground.length - 1)
  453. })
  454. if (!rooms.length || !p1) return hs
  455. let pointId = line.points[0].__id || p1.id
  456. if (line.isOut) {
  457. for (let i = 0; i < rooms.length; i++) {
  458. let index, hole = rooms[i].hole.find(({pos: h}) => ~(index = h.indexOf(pointId)))
  459. if (hole) hs.push({ pos: index, arr: hole.pos})
  460. }
  461. } else {
  462. for (let i = 0; i < rooms.length; i++) {
  463. let index = rooms[i].ground.indexOf(pointId)
  464. if (~index) hs.push({ pos: index, arr: rooms[i].ground })
  465. }
  466. }
  467. return hs
  468. }
  469. destroy() {
  470. let elesArr = this.attrs.map(attr => this[attr])
  471. this.data = {
  472. vertex: [],
  473. wall: [],
  474. room: [],
  475. window: [],
  476. column: [],
  477. door: [],
  478. surplus: []
  479. }
  480. elesArr.forEach(eles => {
  481. while (eles.length) {
  482. eles[0].ele.destroy(true)
  483. }
  484. })
  485. this.render.destroy()
  486. this.render = null
  487. }
  488. }
  489. export default Processing