import Render from '../renderer' import Point from '../../core/point' import WallLine from '../../core/wallline' import Casement from '../../architecture/casement' import Door from '../../architecture/door/index' import Column from '../../architecture/column' import { CADElementTS } from '../../core/element' import { ProcessingTS } from '.' export interface _Point { x: number, y:number, id: number } export interface _Line { p1: number, p2: number, id: number } export interface _window { pos: Array, line: number, top: number, bottom: number } export interface _door { pos: Array, line: number, top: number, bottom: number } export interface _column { pos: Array, line: number } export interface _hole {pos: Array, top: number} export type _Ground = Array export interface _room { ground: _Ground, hole: Array<_hole>, top: number, bottom: number } export interface Data {vertex: Array<_Point>, surplus: Array<_Point>, wall: Array<_Line>, window: Array<_window>, door: Array<_door>, column: Array<_column>, room: Array<_room>} export interface StorageProps{ dom: HTMLElement } export interface CADState {elements: Array} export interface rhole extends _hole {bottom: number} type Walls = Array<{ id: number; ele: WallLine }> type Points = Array<{ id: number; ele: Point }> type Cases = Array<{ele: Casement}> type Doors = Array<{ele: Door}> type Columns = Array<{ele: Column}> const numRetain = (n: number, m = 2) => Number(n.toFixed(m)) class Processing { data: Data render: Render points: Points lines: Walls cases: Cases doors: Doors columns: Columns constructor({dom}: StorageProps) { this.render = new Render({ layer: dom, processing: (this as unknown as ProcessingTS) }) this.points = [] this.lines = [] this.cases = [] this.doors = [] this.columns = [] WallLine.initLines(this.render) } getNewPointId = () => Math.max(...this.points.map(({id}) => id)) + 1 getNewLineId = () => Math.max(...this.lines.map(({id}) => id)) + 1 addPoint = ({id, x, y}: _Point) => { let ret = {id, ele: new Point({x, y, renderer: this.render})} this.points.push(ret) this.generateElement(ret.ele) return ret } addLine = ({id, p1, p2}: _Line) => { const rooms = this.data.room const isOut = rooms.some(({hole}) => hole.some(({pos: h}) => ~h.indexOf(p1) && ~h.indexOf(p2))) let ret = { id, ele: new WallLine({ points: [ this.points.find(p => p.id === p1).ele, this.points.find(p => p.id === p2).ele ], renderer: this.render, color: isOut ? 'red': void 0, isOut }) } this.lines.push(ret) this.generateElement(ret.ele) return ret } addCase = ({pos, line, top = null, bottom = null}: _window) => { let ret = { ele: new Casement({ renderer: this.render, attachment: this.lines.find(({id}) => id === line).ele, points: [{x: pos[0], y: pos[1]}, {x: pos[2], y: pos[3]}], top, bottom }) } this.cases.push(ret) this.generateElement(ret.ele) return ret } addDoor = ({pos, line, top = null, bottom = null}: _door) => { let ret = { ele: new Door({ renderer: this.render, attachment: this.lines.find(({id}) => id === line).ele, points: [{x: pos[0], y: pos[1]}, {x: pos[2], y: pos[3]}], top, bottom }) } this.doors.push(ret) this.generateElement(ret.ele) return ret } addColumn = ({pos, line}: _column) => { let ret = { ele: new Column({ renderer: this.render, attachment: this.lines.find(({ id }) => id === line).ele, points: [ {x: pos[0], y: pos[1]}, {x: pos[2], y: pos[3]}, {x: pos[6], y: pos[7]}, {x: pos[4], y: pos[5]} ] }) } this.columns.push(ret) this.generateElement(ret.ele) return ret } // 获取与当前room有关联的所有room getJoinRooms(room: _room) { const checkRooms = [] const queryRooms = (room: _room) => { if (~checkRooms.indexOf(room)) { return [] } else { let rooms = this.data.room.filter(({ground}) => ground.some(id => ~room.ground.indexOf(id))) checkRooms.push(room) rooms.forEach(room => rooms.push(...queryRooms(room))) return rooms } } return Array.from(new Set(queryRooms(room))) } getPointsByRoom (room: _room) { room = this.data.room.find(r => r.ground.join('') === room.ground.join('')) if (room) { return room.ground.map(eid => this.points.find(({id}) => id === eid).ele) } else { return [] } } getPointsByHole (rhole: rhole) { let rhids = rhole.pos.join('') let hole for (let i = 0; i < this.data.room.length; i++) { hole = this.data.room[i].hole.find(hole => hole.pos.join('') === rhids) if(hole) break } if (hole) { return hole.pos.map(eid => this.points.find(({id}) => id === eid).ele) } else { return [] } } getLinesByRoom (room: _room) { room = this.data.room.find(r => r.ground.join('') === room.ground.join('')) if (room) { return this.lines.filter(({ele: line}) => { let p1 = this.points.find(({ele}) => ele === line.points[0]).id let p2 = this.points.find(({ele}) => ele === line.points[1]).id return ~room.ground.indexOf(p1) && ~room.ground.indexOf(p2) }).map(({ele}) => ele) } else { return [] } } getLinesByHole (rhole: rhole) { let rhids = rhole.pos.join('') let hole for (let i = 0; i < this.data.room.length; i++) { hole = this.data.room[i].hole.find(hole => hole.pos.join('') === rhids) if(hole) break } if (hole) { return this.lines.filter(({ele: line}) => { let p1 = this.points.find(({ele}) => ele === line.points[0]).id let p2 = this.points.find(({ele}) => ele === line.points[1]).id return ~hole.pos.indexOf(p1) && ~hole.pos.indexOf(p2) }).map(({ele}) => ele) } else { return [] } } getRoomsByPoint (point: Point) { let p = this.points.find(({ele}) => ele === point) if (p) { return this.data.room.filter(room => ~room.ground.indexOf(p.id)) } else { return [] } } getHolesByPoint (point: Point): Array { let p = this.points.find(({ele}) => ele === point) let holes = [] if (p) { this.data.room.find(room => { room.hole.forEach(hole => { if (~hole.pos.indexOf(p.id)) { holes.push(hole) } }) }) } else { console.error(point) } return holes } getRoomsByLine (line: WallLine) { let p1 = this.points.find(point => point.ele === line.points[0]).id let p2 = this.points.find(point => point.ele === line.points[1]).id return this.data.room.filter(room => ~room.ground.indexOf(p1) && ~room.ground.indexOf(p2)) } getHolesByLine (line: WallLine): Array<_hole & {bottom: number}> { let p1 = this.points.find(point => point.ele === line.points[0]).id let p2 = this.points.find(point => point.ele === line.points[1]).id let holes = [] this.data.room.forEach(room => { room.hole.forEach(hole => { if (~hole.pos.indexOf(p1) && ~hole.pos.indexOf(p2)) { holes.push(hole) } }) }) return holes } getLineId = (line: WallLine) => this.lines.find(({ele}) => ele === line).id getPointId = (point: Point) => this.points.find(({ele}) => ele === point).id // 将数据转换为ele toEles (data: Data) { let {vertex, wall, window, door, column} = data this.data = data vertex.forEach(this.addPoint) wall.forEach(this.addLine) window.forEach(this.addCase) door.forEach(this.addDoor) column.forEach(this.addColumn) this.data.room.forEach(room => { room.hole.forEach(hole => { Object.defineProperty(hole, 'bottom', { get: () => room.bottom }) }) }) this.referElements() } // 将ele转换为data toData (): Data { let vertex = this.points.map(({ele, id}) => ({ id, x: numRetain(ele.x), y: numRetain(ele.y) })) let wall = this.lines.map(({ele, id}) => ({ id, p1: this.getPointId(ele.points[0]), p2: this.getPointId(ele.points[1]) })) let column = this.columns.map(({ele}) => ({ line: this.getLineId(ele.attachment), pos: [ numRetain(ele.points[0].x), numRetain(ele.points[0].y), numRetain(ele.points[1].x), numRetain(ele.points[1].y), numRetain(ele.points[3].x), numRetain(ele.points[3].y), numRetain(ele.points[2].x), numRetain(ele.points[2].y) ] })) let window = this.cases.map(({ele}) =>({ line: this.getLineId(ele.attachment), pos: [ numRetain(ele.linePoints[0].x), numRetain(ele.linePoints[0].y), numRetain(ele.linePoints[1].x), numRetain(ele.linePoints[1].y) ], top: ele.top, bottom: ele.bottom })) let door = this.doors.map(({ele}) =>({ line: this.getLineId(ele.attachment), pos: [ numRetain(ele.linePoints[0].x), numRetain(ele.linePoints[0].y), numRetain(ele.linePoints[1].x), numRetain(ele.linePoints[1].y) ], top: ele.top, bottom: ele.bottom })) return {vertex, wall, window, door, column, room: this.data.room, surplus: this.data.surplus} } referElements() { let eles = [ ...this.lines, ...this.points, ...this.cases, ...this.doors, ...this.columns ] eles.forEach(({ele}) => { this.render.g.removeChild(ele.real) this.render.elements.splice(this.render.elements.indexOf(ele), 1); }) this.generateElements() } // 删除房间 delRoom(cground: Array) { let index = this.data.room.findIndex(({ground}) => cground === ground) if (~index) { let [room] = this.data.room.splice(index, 1) room.ground.forEach((p1Id, i) => { if (i > room.ground.length - 2) return; let p2Id = room.ground[i + 1] let arr = [p1Id, p2Id] let line = this.lines.find(({ele}) => { let p1 = this.points.find(({ele: p}) => p === ele.points[0]) let p2 = this.points.find(({ele: p}) => p === ele.points[1]) return p1 && p2 && ~arr.indexOf(p1.id) && ~arr.indexOf(p2.id) }) line && line.ele.destroy() }) return true } else { return false } } // 删除镂空区域 delHole(cground: Array) { let hIndex = -1; let rindex = this.data.room.findIndex(({hole}) => { hIndex = hole.findIndex(({pos}) => pos === cground) return ~hIndex }) if (~rindex) { let [hole] = this.data.room[rindex].hole.splice(hIndex, 1) hole.pos.forEach((p1Id, i) => { if (i > hole.pos.length - 2) return; let p2Id = hole.pos[i + 1] let arr = [p1Id, p2Id] let line = this.lines.find(({ele}) => { let p1 = this.points.find(({ele: p}) => p === ele.points[0]) let p2 = this.points.find(({ele: p}) => p === ele.points[1]) return p1 && p2 && ~arr.indexOf(p1.id) && ~arr.indexOf(p2.id) }) line && line.ele.destroy() }) return true } else { return false } } // 构建所有ele generateElements() { let eles = [ ...this.lines.map(line => line.ele), ...this.points.map(point => point.ele), ...this.cases.map(c => c.ele), ...this.doors.map(c => c.ele), ...this.columns.map(c => c.ele) ] eles.forEach(this.generateElement) } attrs = [ 'cases', 'doors', 'columns', 'lines', 'points'] // Element加装Destroy方法 retrofitElementDestroy (ele: CADElementTS) { let destroy = ele.destroy if (ele.__load_destroy) return; ele.__load_destroy = true ele.destroy = (...args) => { // rooms字段中删掉 if (!(args as any)[1] && ele instanceof WallLine) { let p1 = this.points.find(pe => pe.ele === ele.points[0]).id let p2 = this.points.find(pe => pe.ele === ele.points[1]).id this.data.room.forEach(room => { if (~room.ground.indexOf(p1) && ~room.ground.indexOf(p2)) { room.ground.splice(room.ground.indexOf(p1), 1) // 如果一个房间或者镂空区域只剩两个点则销毁 if (room.ground.length <= 2) { this.delHole(room.ground) } } room.hole.forEach(hole => { if (~hole.pos.indexOf(p1) && ~hole.pos.indexOf(p2)) { hole.pos.splice(hole.pos.indexOf(p1), 1) // 如果一个房间或者镂空区域只剩两个点则销毁 if (hole.pos.length <= 2) { this.delHole(hole.pos) } } }) }) // let rooms = this.queryLineRoomPos(ele) // for (let i = 0; i < rooms.length; i++) { // let {pos, arr} = rooms[i] // arr.splice(pos, 1) // // 如果一个房间或者镂空区域只剩两个点则销毁 // if (arr.length <= 2) { // this.delHole(arr) || this.delRoom(arr) // } // } } const rep = () => { // 帮自身缓存的数组删除 this.attrs.forEach(attr => { let index = this[attr].findIndex(({ele: e}) => e === ele) if (~index) { this[attr].splice(index, 1) ele.__id = this[attr][index] && this[attr][index].id } }) this.render.remove(ele) } // 需要同步删除还是更新完属性后删除 if ((args as any)[0]) { rep() } else { ele.nextTick(rep) } destroy.call(ele, ...args) ele.__load_destroy = false } } // Element加装Destroy方法 retrofitElementIntercept (ele: WallLine) { let intercept = ele.intercept if (ele.__load_intercept) return; ele.__load_intercept = true ele.intercept = (...args) => { let isPoints = Object.keys(args[1]).indexOf('points') // 如果更改了points点要同步到rooms if (~isPoints) { let oldIds = ele.points.map(p => this.points.find(({ele}) => ele === p).id) let rooms = this.queryLineRoomPos(ele) ele.nextTick(() => { let newIds = ele.points.map(p => this.points.find(({ele}) => ele === p).id) rooms.forEach(({arr: ground}) => { let oindex1 = ground.indexOf(oldIds[0]) let oindex2 = ground.indexOf(oldIds[1]) ~oindex1 && (ground[oindex1] = newIds[0]) ~oindex2 && (ground[oindex2] = newIds[1]) }) }) } return intercept.apply(ele, args) } } generateElement = (ele: CADElementTS) => { this.render.push(ele) this.retrofitElementDestroy(ele) ele instanceof WallLine && this.retrofitElementIntercept(ele) } // 查找线段第一个点在rooms中的位置 queryLineRoomPos (line: WallLine) { let rooms = this.data.room let p1 = this.points.find(pe => pe.ele === line.points[0]) let p2 = this.points.find(pe => pe.ele === line.points[1]) let hs = [] rooms = rooms.filter((room, i) => { let i1 = room.ground.indexOf(p1.id) let i2 = room.ground.indexOf(p2.id) let j = Math.abs(i1 - i2) return ~i1 && ~i2 && (j === 1 || j === room.ground.length - 1) }) if (!rooms.length || !p1) return hs let pointId = line.points[0].__id || p1.id if (line.isOut) { for (let i = 0; i < rooms.length; i++) { let index, hole = rooms[i].hole.find(({pos: h}) => ~(index = h.indexOf(pointId))) if (hole) hs.push({ pos: index, arr: hole.pos}) } } else { for (let i = 0; i < rooms.length; i++) { let index = rooms[i].ground.indexOf(pointId) if (~index) hs.push({ pos: index, arr: rooms[i].ground }) } } return hs } destroy() { let elesArr = this.attrs.map(attr => this[attr]) this.data = { vertex: [], wall: [], room: [], window: [], column: [], door: [], surplus: [] } elesArr.forEach(eles => { while (eles.length) { eles[0].ele.destroy(true) } }) this.render.destroy() this.render = null } } export default Processing