/* * ********************************************************************************************************************* * * !! * .F88X * X8888Y * .}888888N; * i888888N; .:! .I$WI: * R888888I .'N88~ i8}+8Y&8"l8i$8>8W~'>W8}8]KW+8IIN"8& * .R888888I .;N8888~ .X8' "8I.!,/8" !%NY8`"8I8~~8>,88I * +888888N; .8888888Y "&&8Y.}8, * ./888888N; .R888888Y .'}~ .>}'.`+> i}! "i' +/' .'i~ !11,.:">, .~]! .i}i * ~888888%: .I888888l .]88~`1/iY88Ii+1'.R$8$8]"888888888> Y8$ W8E X8E W8888'188Il}Y88$* * 18888888 E8888881 .]W%8$`R8X'&8%++N8i,8N%N8+l8%` .}8N:.R$RE%N88N%N$K$R 188,FE$8%~Y88I * .E888888I .i8888888' .:$8I;88+`E8R:/8N,.>881.`$8E/1/]N8X.Y8N`"KF&&FK!'88*."88K./$88%RN888+~ * 8888888I .,N888888~ ~88i"8W,!N8*.I88.}888%F,i$88"F88" 888:E8X.>88!i88>`888*.}Fl1]*}1YKi' * i888888N' I888Y ]88;/EX*IFKFK88X K8R .l8W 88Y ~88}'88E&%8W.X8N``]88!.$8K .:W8I * .i888888N; I8Y .&8$ .X88! i881.:%888>I88 ;88] +88+.';;;;:.Y88X 18N.,88l .+88/ * .:R888888I * .&888888I Copyright (c) 2009-2020. 博锐尚格科技股份有限公司 * ~8888' * .!88~ All rights reserved. * * ********************************************************************************************************************* */ import { SGraphItem, SGraphPointListDelete, SGraphPointListInsert, SGraphPointListUpdate, SLineStyle } from "@persagy-web/graph"; import { SKeyCode, SMouseEvent, SUndoStack } from "@persagy-web/base/"; import { SColor, SLine, SLineCapStyle, SPainter, SPoint, SPolygonUtil, SRect } from "@persagy-web/draw"; import { SItemStatus, ItemOrder } from "@persagy-web/big"; import { Marker } from "../type/Marker"; import { SMathUtil } from "@persagy-web/big/lib/utils/SMathUtil"; import { SGraphEdit } from ".." /** * 编辑多边形 * * @author 韩耀龙 */ export class SBasePolygonEdit extends SGraphEdit { /** X坐标最小值 */ private minX = Number.MAX_SAFE_INTEGER; /** X坐标最大值 */ private maxX = Number.MIN_SAFE_INTEGER; /** Y坐标最小值 */ private minY = Number.MAX_SAFE_INTEGER; /** Y坐标最大值 */ private maxY = Number.MIN_SAFE_INTEGER; /** 图例节点对象数据 */ data: Marker; /** 轮廓线坐标 */ private pointList: SPoint[] = []; // 获取当前状态 get getPointList(): SPoint[] { return this.pointList; } // 编辑当前状态 set setPointList(arr: SPoint[]) { this.pointList = arr; this.update(); } /** 当前状态 */ protected _status: number = SItemStatus.Normal; get status(): SItemStatus { return this._status; } set status(value: SItemStatus) { this._status = value; this.undoStack.clear(); this.update(); } /** 边框颜色 */ _strokeColor: SColor = SColor.Black; /** 画笔颜色 */ get strokeColor(): SColor { return this._strokeColor; } // Get strokeColor set strokeColor(v: SColor) { this._strokeColor = v; this.update(); } // Set strokeColor /** 填充颜色 */ _fillColor: SColor = new SColor("#2196f3"); get fillColor(): SColor { return this._fillColor; } // Get fillColor set fillColor(v: SColor) { this._fillColor = v; this.update(); } // Set fillColor /** 边框样式 */ _lineStyle: SLineStyle = SLineStyle.Solid; get lineStyle(): SLineStyle { return this._lineStyle; } // Get lineStyle set lineStyle(v: SLineStyle) { this._lineStyle = v; this.update(); } // Set lineStyle /** 边框的宽 只可输入像素宽 */ _lineWidth: number = 1; get lineWidth(): number { return this._lineWidth; } // Get lineWidth set lineWidth(v: number) { this._lineWidth = v; this.update(); } // Set lineWidth /** 是否闭合 */ closeFlag: boolean = false; /** 鼠标移动点 */ private lastPoint: SPoint | null = null; /** 当前鼠标获取顶点对应索引 */ private curIndex: number = -1; /** 当前鼠标获取顶点对应坐标 */ private curPoint: null | SPoint = null; /** 灵敏像素 */ private len: number = 10; /** 场景像素 内部将灵敏像素换算为场景实际距离 */ private scenceLen: number = 15; /** 场景像素 */ private isAlt: boolean = false; /** undo/redo堆栈 */ protected undoStack: SUndoStack = new SUndoStack(); /** * 构造函数 * * @param parent 指向父对象 * @param data 图例节点对象数据 */ constructor(parent: SGraphItem | null, data: Marker) { super(parent); // this.textItem.originPosition = STextOrigin.Centrum; // this.textItem.isTransform = false; this.zOrder = ItemOrder.polygonOrder; this.data = data; this.name = data.name; // this.text = data.Name; if (data.style) { this.setPointList = []; let setPointList: SPoint[]; if (data.style.outLine) { if (data.style.outLine[0] instanceof SPoint) { // @ts-ignore this.setPointList = data.style.outLine; } else { setPointList = data.style.outLine.map(i => { return (new SPoint(i.x, i.y)) }) this.setPointList = setPointList; } } // 样式相关 if (data.style.default) { // 颜色 if (data.style.default.strokeColor) { this.strokeColor = new SColor(data.style.default.strokeColor) } // 线宽 if (data.style.default.lineWidth) { this.lineWidth = data.style.default.lineWidth } // 线样式 if (data.style.default.lineStyle) { this.lineStyle = data.style.default.lineStyle } // 填充色 if (data.style.default.fillColor) { this.fillColor = new SColor(data.style.default.fillColor) } } } // if (data) { // if (data.Properties.Zorder) { // this.zOrder = data.Properties.Zorder; // } // // 设置线宽 // if (data.Properties.TextPos) { // this.textItem.moveTo(data.Properties.TextPos.X, data.Properties.TextPos.Y); // } // if (data.Properties.color) { // this.color = new SColor(data.Properties.color); // } // if (data.Properties.font) { // this.font = new SFont("sans-serif", data.Properties.font); // } // if (data.Properties && data.Properties.IsActive) { // this.isActive = data.Properties.IsActive; // } // } // 监听多边形创建完成事件,并动态计算文本位置 // this.connect("finishCreated", this, () => { // // 计算文本位置 // let x: number = this.getPointList.reduce((pre, cur, index, arr) => { // return pre + (cur.x / arr.length) // }, 0), // y: number = this.getPointList.reduce((pre, cur, index, arr) => { // return pre + (cur.y / arr.length) // }, 0); // this.textItem.moveTo(x, y); // }) } ////////////////// // 以下为对pointList 数组的操作方法 /** * 储存新的多边形顶点 * * @param x 点位得x坐标 * @param y 点位得y坐标 * @param i 储存所在索引 * @return SPoint。添加的顶点 */ insertPoint(x: number, y: number, i: number | null = null): SPoint { const point = new SPoint(x, y); if (i == null) { this.pointList.push(point); } else { this.pointList.splice(i, 0, point); } this.update(); return point; } // Function insertPoint() /** * 删除点位 * * @param i 删除点所在的索引 * @return SPoint|null。索引不在数组范围则返回null */ deletePoint(i: number | null = null): SPoint | null { let point = null; if (i != null) { if (i >= this.pointList.length || i < 0) { point = null; } else { point = new SPoint(this.pointList[i].x, this.pointList[i].y); this.pointList.splice(i, 1); } } else { if (this.pointList.length) { point = this.pointList[this.pointList.length - 1]; this.pointList.pop(); } else { point = null; } } this.curIndex = -1; this.curPoint = null; this.update(); return point; } // Function deletePoint() /** * 多边形顶点的移动位置 * * @param x 点位得x坐标 * @param y 点位得y坐标 * @param i 点位得i坐标 * @return 移动对应得点。如果索引无法找到移动顶点,则返回null */ movePoint(x: number, y: number, i: number): SPoint | null { let point = null; if (i >= this.pointList.length || i < 0) { return null; } if (this.pointList.length) { this.pointList[i].x = x; this.pointList[i].y = y; } point = this.pointList[i]; return point; } // Function movePoint() //////////// // 以下为三种状态下的绘制多边形方法 /** * 展示状态 --绘制多边形数组 * * @param painter 绘制类 * @param pointList 绘制多边形数组 */ protected drawShowPolygon(painter: SPainter, pointList: SPoint[]): void { painter.save(); painter.pen.lineCapStyle = SLineCapStyle.Square; painter.pen.color = this.strokeColor; painter.brush.color = this.fillColor; painter.pen.lineWidth = painter.toPx(this.lineWidth); if (this.lineStyle == SLineStyle.Dashed) { painter.pen.lineDash = [ painter.toPx(this.lineWidth * 3), painter.toPx(this.lineWidth * 7) ]; } else if (this.lineStyle == SLineStyle.Dotted) { painter.pen.lineDash = [ painter.toPx(this.lineWidth), painter.toPx(this.lineWidth) ]; } if (this.selected) { painter.shadow.shadowBlur = 10; painter.shadow.shadowColor = new SColor(`#00000033`); painter.shadow.shadowOffsetX = 5; painter.shadow.shadowOffsetY = 5; } else { painter.shadow.shadowColor = SColor.Transparent; } painter.drawPolygon([...pointList]); painter.restore(); } // Function drawShowPolygon() /** * 创建状态 --绘制多边形数组 * * @param painter 绘制类 * @param pointList 绘制多边形数组 */ protected drawCreatePolygon(painter: SPainter, pointList: SPoint[]): void { painter.pen.lineCapStyle = SLineCapStyle.Square; painter.pen.color = this.strokeColor; painter.pen.lineWidth = painter.toPx(this.lineWidth); if (this.lastPoint && pointList.length) { painter.drawLine( pointList[pointList.length - 1].x, pointList[pointList.length - 1].y, this.lastPoint.x, this.lastPoint.y ); } painter.drawPolyline(pointList); painter.pen.color = SColor.Transparent; painter.brush.color = new SColor(this.fillColor.value); painter.pen.lineWidth = painter.toPx(this.lineWidth); if (this.lastPoint) { painter.drawPolygon([...pointList, this.lastPoint]); // 绘制顶点块 painter.pen.color = SColor.Black; painter.brush.color = SColor.White; pointList.forEach(item => { painter.drawCircle(item.x, item.y, painter.toPx(this.len / 2)); }); // 如果最后一个点在第一个点的灵敏度范围内,第一个点填充变红 if (this.pointList.length) { if ( SMathUtil.pointDistance( this.lastPoint.x, this.lastPoint.y, this.pointList[0].x, this.pointList[0].y ) < this.scenceLen ) { // 绘制第一个点的顶点块 painter.pen.color = SColor.Black; painter.brush.color = SColor.Red; painter.drawCircle( this.pointList[0].x, this.pointList[0].y, painter.toPx(this.len / 2) ); } } } else { painter.drawPolygon(pointList); } } // Function drawCreatePolygon() /** * * 编辑状态 --绘制多边形数组 * * @param painter 绘制类 * @param pointList 绘制多边形数组 */ protected drawEditPolygon(painter: SPainter, pointList: SPoint[]): void { // 展示多边形 painter.pen.lineCapStyle = SLineCapStyle.Square; painter.pen.color = this.strokeColor; painter.pen.lineWidth = painter.toPx(this.lineWidth); painter.brush.color = new SColor(this.fillColor.value); painter.drawPolygon([...pointList]); // 绘制顶点块 painter.pen.color = SColor.Black; painter.brush.color = SColor.White; pointList.forEach((item, index) => { painter.brush.color = SColor.White; if (index == this.curIndex) { painter.brush.color = new SColor("#2196f3"); } painter.drawCircle(item.x, item.y, painter.toPx(this.len / 2)); }); } // Function drawCreatePolygon() /** * 编辑状态操作多边形数组 * * @param event 鼠标事件 * * */ protected editPolygonPoint(event: SMouseEvent): void { // 判断是否为删除状态 isAlt = true为删除状态 if (this.isAlt) { // 1 判断是否点击在多边形顶点 let lenIndex = -1; // 当前点击到的点位索引; let curenLen = this.scenceLen; // 当前的灵敏度 this.pointList.forEach((item, index) => { let dis = SMathUtil.pointDistance( event.x, event.y, item.x, item.y ); if (dis < curenLen) { curenLen = dis; lenIndex = index; } }); // 若点击到,对该索引对应的点做删除 if (lenIndex != -1) { if (this.pointList.length <= 3) { return; } const delePoint = new SPoint( this.pointList[lenIndex].x, this.pointList[lenIndex].y ); this.deletePoint(lenIndex); // 记录顶点操作记录压入堆栈 this.recordAction(SGraphPointListDelete, [ this.pointList, delePoint, lenIndex ]); } } else { // 1 判断是否点击在多边形顶点 this.curIndex = -1; this.curPoint = null; let lenIndex = -1; // 当前点击到的点位索引; let curenLen = this.scenceLen; // 当前的灵敏度 this.pointList.forEach((item, index) => { let dis = SMathUtil.pointDistance( event.x, event.y, item.x, item.y ); if (dis < curenLen) { curenLen = dis; lenIndex = index; } }); this.curIndex = lenIndex; // 2判断是否点击在多边形得边上 if (-1 == lenIndex) { let len = SMathUtil.pointToLine( new SPoint(event.x, event.y), new SLine(this.pointList[0], this.pointList[1]) ), index = 0; if (this.pointList.length > 2) { for (let i = 1; i < this.pointList.length; i++) { let dis = SMathUtil.pointToLine( new SPoint(event.x, event.y), new SLine(this.pointList[i], this.pointList[i + 1]) ); if (i + 1 == this.pointList.length) { dis = SMathUtil.pointToLine( new SPoint(event.x, event.y), new SLine(this.pointList[i], this.pointList[0]) ); } if (dis.MinDis < len.MinDis) { len = dis; index = i; } } } // 判断是否有点 if (len.Point) { // 点在了多边形的边上 if (len.MinDis <= this.scenceLen) { this.pointList.splice(index + 1, 0, len.Point); // 记录新增顶点操作记录压入堆栈 this.recordAction(SGraphPointListInsert, [ this.pointList, len.Point, index + 1 ]); } else { //没点在多边形边上也没点在多边形顶点上 super.onMouseDown(event); } } } else { // 当捕捉到顶点后 ,记录当前点的xy坐标,用于undo、redo操作 this.curPoint = new SPoint( this.pointList[this.curIndex].x, this.pointList[this.curIndex].y ); } // 刷新视图 this.update(); } } // Function editPolygonPoint() ///////////////////// // undo、redo相关操作 /** * 记录相关动作并推入栈中 * @param SGraphCommand 相关命令类 * @param any 对应传入参数 */ protected recordAction(SGraphCommand: any, any: any[]): void { // 记录相关命令并推入堆栈中 const sgraphcommand = new SGraphCommand(this.scene, this, ...any); this.undoStack.push(sgraphcommand); } // Function recordAction() /** * 执行取消操作执行 */ undo(): void { if (this.status == SItemStatus.Normal) { return; } this.undoStack.undo(); } // Function undo() /** * 执行重做操作执行 */ redo(): void { if (this.status == SItemStatus.Normal) { return; } this.undoStack.redo(); } // Function redo() /////////////////////////////// // 以下为鼠标事件 /** * 鼠标双击事件 * * @param event 事件参数 * @return boolean */ onDoubleClick(event: SMouseEvent): boolean { // 如果位show状态 双击改对象则需改为编辑状态 if (SItemStatus.Normal == this.status) { this.status = SItemStatus.Edit; this.grabItem(this); } else if (SItemStatus.Edit == this.status) { this.status = SItemStatus.Normal; this.releaseItem(); } this.update(); return true; } // Function onDoubleClick() /** * 键盘事件 * * @param event 事件参数 * @return boolean */ onKeyDown(event: KeyboardEvent): boolean { if (this.status == SItemStatus.Normal) { return false; } else if (this.status == SItemStatus.Create) { if (event.code == "Enter") { // 当顶点大于二个时才又条件执行闭合操作并清空堆栈 if (this.pointList.length > 2) { this.status = SItemStatus.Normal; //3 传递完成事件状态 this.$emit("finishCreated"); //1 grabItem 置为null this.releaseItem(); } } } else if (this.status == SItemStatus.Edit) { if (event.key == "Alt") { this.isAlt = true; } } this.update(); return true; } // Function onKeyDown() /** * 键盘键抬起事件 * * @param event 事件参数 * @return boolean */ onKeyUp(event: KeyboardEvent): void { if (this.status == SItemStatus.Edit) { if (event.key == "Alt") { this.isAlt = false; } else if (event.keyCode == SKeyCode.Delete) { // 当多边形的顶点大于三个允许删除点 if (this.pointList.length > 3) { this.deletePoint(this.curIndex); } } } this.update(); } // Function onKeyUp() /** * 鼠标按下事件 * * @param event 事件参数 * @return boolean */ onMouseDown(event: SMouseEvent): boolean { if (event.shiftKey) { event = this.compare(event); } // 如果状态为编辑状态则添加点 if (this.status == SItemStatus.Create) { // 新增顶点 let len = -1; if (this.pointList.length) { len = SMathUtil.pointDistance( event.x, event.y, this.pointList[0].x, this.pointList[0].y ); } if (this.pointList.length > 2 && len > 0 && len < this.scenceLen) { this.status = SItemStatus.Normal; //3 传递完成事件状态 this.$emit("finishCreated"); //1 grabItem 置为null this.releaseItem(); } else { this.insertPoint(event.x, event.y); // 记录新增顶点操作记录压入堆栈 let pos = new SPoint(event.x, event.y); this.recordAction(SGraphPointListInsert, [this.pointList, pos]); } } else if (this.status == SItemStatus.Edit) { // 对多边形数组做编辑操作 this.editPolygonPoint(event); } else { return super.onMouseDown(event); } return true; } // Function onMouseDown() /** * 鼠标移入事件 * * @param event 事件参数 * @return boolean */ onMouseEnter(event: SMouseEvent): boolean { return true; } // Function onMouseEnter() /** * 鼠标移出事件 * * @param event 事件参数 * @return boolean */ onMouseLeave(event: SMouseEvent): boolean { return true; } // Function onMouseLeave() /** * 鼠标移动事件 * * @param event 事件参数 * @return boolean */ onMouseMove(event: SMouseEvent): boolean { if (event.shiftKey) { event = this.compare(event); } if (this.status == SItemStatus.Create) { this.lastPoint = new SPoint(); this.lastPoint.x = event.x; this.lastPoint.y = event.y; this.update(); } else if (this.status == SItemStatus.Edit) { if (event.buttons == 1) { if (-1 != this.curIndex) { this.pointList[this.curIndex].x = event.x; this.pointList[this.curIndex].y = event.y; } } this.update(); } else { return super.onMouseMove(event); } return true; } // Function onMouseMove() /** * shift垂直水平创建或编辑 * * @param event 事件 * */ compare(event: SMouseEvent): SMouseEvent { if (this.pointList.length) { let last = new SPoint(event.x, event.y); if (this.status == SItemStatus.Create) { last = this.pointList[this.pointList.length - 1]; } else if (this.status == SItemStatus.Edit) { if (this.curIndex > 0) { last = this.pointList[this.curIndex - 1]; } } const dx = Math.abs(event.x - last.x); const dy = Math.abs(event.y - last.y); if (dy > dx) { event.x = last.x; } else { event.y = last.y; } } return event; } // Function compare() /** * 鼠标抬起事件 * * @param event 事件参数 * @return boolean */ onMouseUp(event: SMouseEvent): boolean { if (this.status == SItemStatus.Edit) { if (-1 != this.curIndex) { const pos = new SPoint( this.pointList[this.curIndex].x, this.pointList[this.curIndex].y ); this.recordAction(SGraphPointListUpdate, [ this.pointList, this.curPoint, pos, this.curIndex ]); } } else if (this.status == SItemStatus.Normal) { this.moveToOrigin(this.x, this.y); return super.onMouseUp(event); } return true; } // Function onMouseUp() /** * 移动后处理所有坐标,并肩原点置为场景原点 * * @param x x坐标 * @param y y坐标 * */ moveToOrigin(x: number, y: number): void { super.moveToOrigin(x, y); this.pointList = this.pointList.map(t => { t.x = t.x + x; t.y = t.y + y; return t; }); this.x = 0; this.y = 0; } // Function moveToOrigin() /** * 适配事件 * * @param event 事件参数 * @return boolean */ onResize(event: SMouseEvent): boolean { return true; } // Function onResize() /** * 取消操作 * */ cancelOperate(): void { // 当状态为展示状态 if (this.status == SItemStatus.Create) { // 闭合多边形 this.parent = null; } else if (this.status == SItemStatus.Edit) { // 编辑状态 this.status = SItemStatus.Normal; } this.update(); } // Function cancelOperate() /** * Item对象边界区域 * * @return SRect */ boundingRect(): SRect { if (this.pointList.length) { this.minX = this.pointList[0].x; this.maxX = this.pointList[0].x; this.minY = this.pointList[0].y; this.maxY = this.pointList[0].y; this.pointList.forEach(it => { let x = it.x, y = it.y; if (x < this.minX) { this.minX = x; } if (y < this.minY) { this.minY = y; } if (x > this.maxX) { this.maxX = x; } if (y > this.maxY) { this.maxY = y; } }); } return new SRect( this.minX, this.minY, this.maxX - this.minX, this.maxY - this.minY ); } // Function boundingRect() /** * 判断点是否在区域内 * * @param x * @param y */ contains(x: number, y: number): boolean { let arr = this.pointList; if (arr.length < 3 || !SPolygonUtil.pointIn(x, y, arr)) { return false; } return true; } // Function contains() /** * Item绘制操作 * * @param painter painter对象 */ onDraw(painter: SPainter): void { this.scenceLen = painter.toPx(this.len); // 当状态为展示状态 if (this.status == SItemStatus.Normal) { // 闭合多边形 this.drawShowPolygon(painter, this.pointList); } else if (this.status == SItemStatus.Create) { // 创建状态 this.drawCreatePolygon(painter, this.pointList); } else { // 编辑状态 this.drawEditPolygon(painter, this.pointList); } } // Function onDraw() } // Class SPolygonItem