import { SMouseEvent } from "@persagy-web/base/lib"; import { SMathUtil } from "@persagy-web/big/lib/utils/SMathUtil"; import { SPoint, SRect } from "@persagy-web/draw/lib"; import { FloorScene } from "./FloorScene"; import { HighlightItem } from "./HighlightItem"; import { ShadeItem } from "./ShadeItem"; // @ts-ignore import { intersect, polygon, segments, combine, selectIntersect, selectUnion, selectDifference, selectDifferenceRev, difference } from "polybooljs"; import { DrawZoneItem } from "./DrawZoneItem"; import { SItemStatus, unzip } from "@persagy-web/big/lib"; import { Wall } from "@persagy-web/big/lib/types/floor/Wall"; import { CustomWall } from "./CustomWall"; import { SGraphItem, SGraphStyleItem } from "@persagy-web/graph/lib"; import { SWallItem } from "@persagy-web/big/lib/items/floor/SWallItem"; // @ts-ignore import pako from "pako"; export class DivideFloorScene extends FloorScene { /** 划分item */ cutItem: ShadeItem | null = null; /** 绘制的分区item */ drawItem: DrawZoneItem | null = null; /** 当前绘制命令 cut-交集区域绘制 zoneDraw-绘制业务空间 wallDraw-画墙 */ // 2021.3.4需求不算交集,直接绘制业务空间 drawCmd: string = ''; /** 是否开启吸附 */ private _isAbsorbing: boolean = false; get isAbsorbing(): boolean { return this._isAbsorbing; } // Get isAbsorbing set isAbsorbing(v: boolean) { this._isAbsorbing = v; } // Set isAbsorbing /** 高亮item */ highLight: HighlightItem | null = null; /** 删除日志 */ deleteWallLog: Wall[] = [] /** 用户自己增加的墙item */ customWall: CustomWall[] = [] /** * 清除划分区域 */ clearCut(): void { if (this.cutItem && this.drawCmd == 'cut') { this.grabItem = null; this.removeItem(this.cutItem); this.cutItem = null; this.view && this.view.update(); } if (this.drawItem && this.drawCmd == 'zoneDraw') { this.grabItem = null; this.removeItem(this.drawItem); this.drawItem = null; this.view && this.view.update(); } this.drawCmd = ''; } // Function clearCut() /** * 清除绘制的墙 */ clearWalls() { if (this.customWall.length) { this.customWall.forEach(t => { this.removeItem(t) }) this.customWall = [] this.grabItem = null; } } /** * 删除墙功能 */ delWall() { if (this.grabItem && this.grabItem instanceof CustomWall && this.grabItem.status == SItemStatus.Create) { this.deleteItem(this.grabItem); this.grabItem = null; } else if (this.selectContainer.count > 0) { const item = this.selectContainer.itemList[0]; if (item instanceof SWallItem || item instanceof CustomWall) { this.deleteItem(item); this.selectContainer.itemList.shift(); } } } /** * 删除墙 */ deleteItem(item: SGraphItem) { if (item instanceof CustomWall) { this.removeItem(item); for (let i = 0; i < this.customWall.length; i++) { if (this.customWall[i] == item) { this.customWall.splice(i, 1) break } } this.view?.update() } else if (item instanceof SWallItem) { this.removeItem(item); this.deleteWallLog.push(item.data) this.view?.update() } } /** * 鼠标按下事件 * * @param event 保存事件参数 * @return boolean */ onMouseDown(event: SMouseEvent): boolean { // 判断是否开启吸附,并且有吸附的点 if ( this.isAbsorbing && this.highLight && this.highLight.visible ) { event.x = this.highLight.point.x; event.y = this.highLight.point.y; } if (this.grabItem) { return this.grabItem.onMouseDown(event); } if (event.buttons == 1) { if (this.drawCmd == 'cut') { if (!this.cutItem) { let point = new SPoint(event.x, event.y); let item = new ShadeItem(null, point); this.addItem(item); this.cutItem = item; this.grabItem = item; return true; } } else if (this.drawCmd == 'zoneDraw') { if (!this.drawItem) { let point = new SPoint(event.x, event.y); let item = new DrawZoneItem(null, point); item.status = SItemStatus.Create; this.addItem(item); this.drawItem = item; this.grabItem = item; return true; } } else if (this.drawCmd == "wallDraw") { let point = new SPoint(event.x, event.y); let item = new CustomWall(null, point) item.status = SItemStatus.Create; this.addItem(item); this.customWall.push(item) item.connect("finishCreated", this, this.finishCreated) this.grabItem = item; return true; } } return super.onMouseDown(event) } /** * 吸附空间 * * @param event 鼠标事件对象 */ onMouseMove(event: SMouseEvent): boolean { super.onMouseMove(event); if (this.isAbsorbing) { if (!this.highLight) { this.highLight = new HighlightItem(null); this.addItem(this.highLight); } this.highLight.visible = false; // if (!this.absorbShade(event)) { this.absorbSpace(event); // } } return false; } // Function onMouseMove() /** * 吸附空间 * * @param event 鼠标事件对象 * @return boolean 是否找到吸附的对象 */ absorbSpace(event: SMouseEvent): boolean { if (!this.highLight) { return false; } let absorbLen = 1000; if (this.view) { absorbLen = 10 / this.view.scale; } let P = this.absorbSpacePoint(event, absorbLen); if (P.Point) { this.highLight.distance = P.MinDis; this.highLight.point = new SPoint(P.Point.X, -P.Point.Y); this.highLight.visible = true; return true; } else { let L = this.absorbSpaceLine(event, absorbLen); if (L.Line && L.Point) { this.highLight.distance = L.MinDis; this.highLight.point = L.Point; this.highLight.line = L.Line; this.highLight.visible = true; return true; } return false; } } // Function absorbSpace() /** * 吸附空间点 * * @param event 鼠标事件对象 * @param absorbLen 吸附距离 * @return MinDis 吸附的点 */ absorbSpacePoint(event: SMouseEvent, absorbLen: number): any { let minPointDis = Number.MAX_SAFE_INTEGER; let Point; this.spaceList.map((space): void => { if ( DivideFloorScene.isPointInAbsorbArea( new SPoint(event.x, event.y), space.minX, space.maxX, space.minY, space.maxY ) ) { space.data.OutLine.forEach((item): void => { let minDis = SMathUtil.getMinDisPoint( new SPoint(event.x, event.y), item ); if ( minDis && minDis.MinDis < absorbLen && minDis.MinDis < minPointDis ) { minPointDis = minDis.MinDis; Point = minDis.Point; } }); } }); return { MinDis: minPointDis, Point: Point }; } // Function absorbSpacePoint() /** * 点是否在吸附区域内 * * @param p 要判断的点 * @param minX 空间区域 * @param minY 空间区域 * @param maxX 空间区域 * @param maxY 空间区域 */ static isPointInAbsorbArea( p: SPoint, minX: number, maxX: number, minY: number, maxY: number ): boolean { let rect = new SRect( minX - 1000, minY - 1000, maxX - minX + 2000, maxY - minY + 2000 ); return rect.contains(p.x, p.y); } // Function isPointInAbsorbArea() /** * 吸附空间线 * * @param event 鼠标事件对象 * @param absorbLen 吸附距离 * @return PointToLine 吸附的线 */ absorbSpaceLine(event: SMouseEvent, absorbLen: number): any { let minPointDis = Number.MAX_SAFE_INTEGER; let Point, Line; this.spaceList.forEach((space): void => { if ( DivideFloorScene.isPointInAbsorbArea( new SPoint(event.x, event.y), space.minX, space.maxX, space.minY, space.maxY ) ) { space.data.OutLine.forEach((item): void => { let minDisLine = SMathUtil.getMinDisLine( new SPoint(event.x, event.y), item ); if ( minDisLine && minDisLine.MinDis < absorbLen && minDisLine.MinDis < minPointDis ) { minPointDis = minDisLine.MinDis; Point = minDisLine.Point; Line = minDisLine.Line; } }); } }); return { MinDis: minPointDis, Point: Point, Line: Line }; } // Function absorbSpaceLine() /** * 计算选中的空间与业务空间的差集 */ getSpaceZoneIntersect(): SPoint[][] | undefined { // 未选中空间- 不计算 if (!this.selectContainer.itemList.length) { return } // 没有业务空间不计算 if (!this.zoneList.length) { return } // 生成选中的空间计算时用的格式 const space = { regions: [], inverted: false } const sourceIdToDiff = {}; try { let poly1, poly2; const start = +new Date(); let rect1: SRect, list = []; // 计算外接矩阵与选中空间相交的业务空间的所有轮廓 this.selectContainer.itemList.forEach(item => { rect1 = item.boundingRect(); this.zoneList.forEach(t => { if (t.visible) { let rect2 = t.boundingRect(); // 外接矩阵是否相交,缩小计算范围 if (SMathUtil.rectIntersection(rect1, rect2)) { t.pointList.forEach((zoneLine): void => { let polygons = { regions: [], inverted: false }; zoneLine.forEach((po): void => { let point = SMathUtil.transferToArray(po); polygons.regions.push(point); }); list.push(polygons); }) } } }) }) // if (list.length) { let seg1 = segments(list[0]), seg2, comb; for (let i = 1; i < list.length; i++) { seg2 = segments(list[i]); comb = combine(seg1, seg2); seg1 = selectUnion(comb); } poly1 = seg1; this.selectContainer.itemList.forEach(t => { let key = t.data.SourceId; poly2 = { regions: [], inverted: false } poly2.regions = t.pointList[0].map((item): number[][] => { return SMathUtil.transferToArray(item); }); const comb = combine(segments(poly2), poly1) const diffObj = selectDifference(comb); const diffPoly = polygon(diffObj) // 理论上只要选择了空间 length就不会为0 if (diffPoly.regions.length) { let outlineList = SMathUtil.getIntersectInArray( diffPoly.regions ); console.log(outlineList); // @ts-ignore sourceIdToDiff[key] = outlineList.map(t => { let arr = [t.Outer]; t.Inner.forEach((inner): void => { arr.push(inner); }); return arr; }); } }); const end = +new Date() console.log(sourceIdToDiff); console.log(end - start, 'comb-diff'); return sourceIdToDiff } } catch (err) { console.log(err); } } /** * 计算选中的空间与绘制的区域交集 */ getSpaceCutIntersect(seg?: any): SPoint[][] | undefined { // 未选中空间- 不计算 if (!this.selectContainer.itemList.length) { return } // 没有绘制不计算 if (!this.cutItem) { return } const sourceIdToIntersect = {}; try { const start = +new Date(); let cutPoly; if (seg) { cutPoly = seg } else { const poly = { regions: [SMathUtil.transferToArray(this.cutItem.pointList)], inverted: false } cutPoly = segments(poly) } this.selectContainer.itemList.forEach(item => { const arr = item.pointList[0].map(t => { return SMathUtil.transferToArray(t); }) const poly2 = { regions: arr, inverted: false } const seg = segments(poly2) const comb = combine(cutPoly, seg) if (item.data.SourceId) { const spoly = polygon(selectIntersect(comb)); // 绘制切割区域可能与空间没有交集 if (spoly.regions.length) { let outlineList = SMathUtil.getIntersectInArray( spoly.regions ); console.log(outlineList); // @ts-ignore sourceIdToIntersect[item.data.SourceId] = outlineList.map(t => { let arr = [t.Outer]; t.Inner.forEach((inner): void => { arr.push(inner); }); return arr; }); // 得到的结果 // sourceIdToIntersect[item.data.SourceId] = polygon(selectIntersect(comb)) } else { // 没有交集 } } }) const end = +new Date() console.log(end - start, 'comb-intersect', sourceIdToIntersect); return sourceIdToIntersect } catch (err) { console.log(err); } return } /** * 如果场景中既有绘制的区域也有创建好的业务空间,则在区域中将已创建的业务空间减去 */ getCurInZone() { if (!this.cutItem) { return } // 绘制区域外接矩阵 const rect2 = this.cutItem.boundingRect() // 绘制区域的多边形 - 也是每次减去业务空间后剩下的多边形 let poly = segments({ regions: [SMathUtil.transferToArray(this.cutItem.pointList)], inverted: false }) this.zoneList.forEach(item => { if (item.visible) { const rect1 = item.boundingRect(); if (SMathUtil.rectIntersection(rect1, rect2)) { item.pointList.forEach((zoneLine): void => { let polygons = { regions: [], inverted: false } zoneLine.forEach((po): void => { let point = SMathUtil.transferToArray(po); polygons.regions.push(point); }); const segZone = segments(polygons); const comb = combine(poly, segZone); poly = selectDifference(comb) }) } } }) console.log(poly); // poly为segments类型 return poly; } /** * 读取场景中的底图对象并生成相应json */ getMapObject(): string { try { if (this.json) { let map = JSON.parse(this.json); const tempWall = map.EntityList[0].Elements.Walls console.log(map.EntityList[0].Elements.Walls.length); const logArr = this.deleteWallLog.map(t => { return t.SourceId }) if (this.deleteWallLog.length) { for (let i = 0; i < tempWall.length; i++) { if (logArr.includes(tempWall[i].SourceId)) { tempWall.splice(i, 1); i--; } } } if (this.customWall.length) { this.customWall.forEach(t => { const data = t.toData(); if (data.OutLine.length) { tempWall.push(data) } }) } console.log(map.EntityList[0].Elements.Walls.length); return JSON.stringify(map); } return '' } catch (err) { console.log(err) return '' } } /** * item绘制完成 */ finishCreated(item: SGraphStyleItem) { this.grabItem = null; // @ts-ignore item.status = SItemStatus.Normal; this.selectContainer.clear(); this.selectContainer.toggleItem(item); this.clearCmdStatus() } /** * 清除命令 */ clearCmdStatus() { this.drawCmd = '' } /** * 生成txt文件流 * * @param fName 文件名 */ generateFile(fName: string, _fn: Function) { if (this.json) { // const zip = new JSZip(); // // // // // // 名称-文件内容 // zip.file(fName, this.json) // // console.log(zip); // zip.generateAsync({ type: "blob", compression: "DEFLATE" }).then(blob => { // // console.log(blob); // unzip(blob); // // this.upload('testmap4.jsonz', blob) // // let url = URL.createObjectURL(blob); // // SNetUtil.downLoad('12312312', url); // let xxx = new JSZip() // xxx.loadAsync(blob).then(res => { // // console.log(res.files) // // for(let i in res.files) { // // console.log(res.files[i]); // // res.files[i].async('nodebuffer').then(con => { // // console.log(con); // // }) // // } // xxx.file('1').async("string").then(resp => { // // console.log(resp) // }) // }); // }, err => { // console.log(err) // }); // const json = this.getMapObject() // 生成压缩的字符串 const bl = this.zip(json); // const blob = new File([bl], '1') // 生成blob对象 const blob = new Blob([bl], { type: 'application/octet-stream' }) // let url = URL.createObjectURL(new Blob([bl])) // SNetUtil.downLoad('12312312', url) // 测试能否正常解压 // unzip(blob); // console.log(blob) // const file = new File([bl], '111') this.upload(fName, blob, _fn) } } /** * 上传 * * @param key 当前显示的楼层平面图的key * @param blob 上传的blob对象 */ upload(key: string, blob: Blob, _fn: Function) { let reader = new FileReader(); reader.onloadstart = function () { // 这个事件在读取开始时触发 console.log('start'); }; reader.onprogress = function (p) { // 这个事件在读取进行中定时触发 console.log('onprogress--------', p); }; reader.onload = function () { // 这个事件在读取成功结束后触发 console.log('onload'); }; reader.onloadend = function () { var xhr = new XMLHttpRequest(); xhr.open( "POST", `/image-service/common/file_upload?systemId=revit&secret=63afbef6906c342b&overwrite=true&key=${key}` ); xhr.send(reader.result); xhr.onreadystatechange = function () { if (xhr.readyState == 4) { if (xhr.status == 200) { _fn() } else { } } }; } reader.readAsArrayBuffer(blob); } /** * 压缩文件 * * @param str 要压缩的字符串 */ zip(str: string): string { var binaryString = pako.deflate(str) // var binaryString = pako.gzip(escape(str), { to: 'string' }); return binaryString; } }