/* * ********************************************************************************************************************* * * !! * .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-2021. 博锐尚格科技股份有限公司 * ~8888' * .!88~ All rights reserved. * * ********************************************************************************************************************* */ import { SColor, SLine, SPainter, SPoint, SRect } from "@persagy-web/draw"; import { SMouseButton, SMouseEvent, SUndoStack } from "@persagy-web/base"; import { SMathUtil } from "../../utils/SMathUtil"; import { SItemStatus } from "../../index"; import { SGraphItem, SGraphPointListInsert, SGraphPointListUpdate, SLineStyle } from "@persagy-web/graph"; import { SGraphStyleItem } from "@persagy-web/graph/lib"; /** * 线段 item * * @author 郝建龙 */ export class SLineItem extends SGraphStyleItem { /** 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; /** 线段 */ private _line: SPoint[] = []; get line(): SPoint[] { return this._line; } set line(arr: SPoint[]) { this._line = arr; this.update(); } /** 是否完成绘制 */ protected _status: SItemStatus = SItemStatus.Normal; get status(): SItemStatus { return this._status; } set status(v: SItemStatus) { this._status = v; this.undoStack.clear(); this.update(); } /** 选中端点填充色 */ private _activeFillColor: SColor = new SColor("#2196f3"); get activeFillColor(): SColor { return this._activeFillColor; } set activeFillColor(v: SColor) { this._activeFillColor = v; this.update(); } /** 拖动灵敏度 */ dis: number = 5; /** 拖动灵敏度 */ private sceneDis: number = 5; /** 当前点索引 */ curIndex: number = -1; /** 当前点坐标 */ private curPoint: SPoint | null = null; /** undo/redo 堆栈 */ private undoStack: SUndoStack = new SUndoStack(); /** * 构造函数 * * @param parent 父级 */ constructor(parent: SGraphItem | null); /** * 构造函数 * * @param parent 父级 * @param line 坐标集合 */ constructor(parent: SGraphItem | null, line: SPoint[]); /** * 构造函数 * * @param parent 父级 * @param point 第一个点坐标 */ constructor(parent: SGraphItem | null, point: SPoint); /** * 构造函数 * * @param parent 父级 * @param line 坐标集合|第一个点坐标 */ constructor(parent: SGraphItem | null, line?: SPoint | SPoint[]) { super(parent); // 坐标集合存在时 if (line) { // 坐标集合出现在 SPoint 的实例对象上 if (line instanceof SPoint) { this.line.push(line); } else { // 没有在 SPoint 的实例对象上 this.line = line; } } else { // 坐标集合不存在时 this.line = []; } } /** * 点发生变化 */ protected pointChange(): void { // do nothing } /** * 添加点至数组中 * * @param p 添加的点 */ private addPoint(p: SPoint): void { // 坐标集合长度大于2时 if (this.line.length < 2) { this.line.push(p); this.recordAction(SGraphPointListInsert, [this.line, p]); } else { // 坐标集合长度不大于2时 this.line[1] = p; this.recordAction(SGraphPointListInsert, [this.line, p, 1]); this.status = SItemStatus.Normal; this.releaseItem(); this.$emit("finishCreated"); this.pointChange(); } this.update(); } /** * 获取点击点与 Point[] 中的点距离最近点 * * @param p 鼠标点击点 */ findNearestPoint(p: SPoint): void { // 拖动灵敏度 let len = this.sceneDis; for (let i = 0; i < this.line.length; i++) { // 计算点到点距离 let dis = SMathUtil.pointDistance( p.x, p.y, this.line[i].x, this.line[i].y ); // 点到点的距离大于灵敏度时 if (dis < len) { len = dis; this.curIndex = i; this.curPoint = new SPoint(this.line[this.curIndex]); } } } /** * 记录相关动作并推入栈中 * * @param SGraphCommand 相关命令类 * @param any 对应传入参数 */ protected recordAction(SGraphCommand: any, any: any[]): void { // 记录相关命令并推入堆栈中 todo const command = new SGraphCommand(this.scene, this, ...any); this.undoStack.push(command); } /** * 移动后处理所有坐标,并肩原点置为场景原点 * * @param x x 坐标 * @param y y 坐标 */ moveToOrigin(x: number, y: number): void { super.moveToOrigin(x, y); // 遍历点集 this.line = this.line.map( (t): SPoint => { t.x = t.x + x; t.y = t.y + y; return t; } ); this.x = 0; this.y = 0; } /** * shift 垂直水平创建或编辑 * * @param event 事件 * @return 处理后的事件对象 */ compare(event: SMouseEvent): SMouseEvent { // 线段长度存在时 if (this.line.length) { let last = new SPoint(event.x, event.y); // 处于创建状态时 if (this.status == SItemStatus.Create) { last = this.line[0]; } else if (this.status == SItemStatus.Edit) { // 处于编辑状态时 // 当前索引等于 1 时 if (this.curIndex == 1) { last = this.line[0]; } else if (this.curIndex == 0) { // 当前索引等于 0 时 last = this.line[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; } /** * 判断点是否在区域内 * * @param x x 坐标 * @param y y 坐标 * @return 是否包含 */ contains(x: number, y: number): boolean { // 线段长度大于 2 时 if (this.line.length == 2) { let p = new SPoint(x, y); // 计算点到线段垂线与线段的交点 if ( SMathUtil.pointToLine(p, new SLine(this.line[0], this.line[1])) .MinDis < this.dis ) { return true; } } return false; } /** * 撤销操作 */ undo(): void { // 处于标准状态时 if (this.status != SItemStatus.Normal) { this.undoStack.undo(); } } /** * 重做操作 */ redo(): void { // 处于标准状态时 if (this.status != SItemStatus.Normal) { this.undoStack.redo(); } } /** * 取消操作 item 事件 */ cancelOperate(): void { // 处于创建状态时 if (this.status == SItemStatus.Create) { this.parent = null; this.releaseItem(); } else if (this.status == SItemStatus.Edit) { // 处于编辑状态时 this.status = SItemStatus.Normal; this.releaseItem(); } } /** * Item 对象边界区域 * * @return 边界区域 */ boundingRect(): SRect { // 线段长度存在时 if (this.line.length) { this.minX = this.line[0].x; this.maxX = this.line[0].x; this.minY = this.line[0].y; this.maxY = this.line[0].y; this.line.forEach((it): void => { let x = it.x, y = it.y; // 如果数据 x 坐标小于 x 坐标最小值 if (x < this.minX) { this.minX = x; } // 如果数据 y 坐标小于 y 坐标最小值 if (y < this.minY) { this.minY = y; } // 如果数据 x 坐标大于 x 坐标最小值 if (x > this.maxX) { this.maxX = x; } // 如果数据 y 坐标大于 y 坐标最小值 if (y > this.maxY) { this.maxY = y; } }); } return new SRect( this.minX, this.minY, this.maxX - this.minX, this.maxY - this.minY ); } /** * Item 绘制操作 * * @param painter 绘制对象 */ onDraw(painter: SPainter): void { this.sceneDis = painter.toPx(this.dis); painter.pen.lineWidth = painter.toPx(this.lineWidth); painter.pen.color = this.strokeColor; // 线段长度等于 2 时 if (this.line.length == 2) { // 绘制直线 painter.pen.color = new SColor(this.strokeColor); // 绘制基本图形,线样式是虚线时 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 && this.status == SItemStatus.Normal) { painter.pen.lineWidth = painter.toPx(this.lineWidth * 2); painter.shadow.shadowBlur = 10; painter.shadow.shadowColor = new SColor(`#00000033`); painter.shadow.shadowOffsetX = 5; painter.shadow.shadowOffsetY = 5; } painter.drawLine(this.line[0], this.line[1]); // 处于编辑状态或创建状态时 if ( this.status == SItemStatus.Edit || this.status == SItemStatus.Create ) { // 绘制端点 this.line.forEach((p, i): void => { painter.brush.color = this.fillColor; if (i == this.curIndex) { painter.brush.color = this.activeFillColor; } painter.drawCircle(p.x, p.y, painter.toPx(5)); }); } } else if (this.line.length == 1) { // 线段长度等于 1 时 // 处于编辑状态或创建状态时 if ( this.status == SItemStatus.Edit || this.status == SItemStatus.Create ) { // 绘制端点 painter.brush.color = this.fillColor; painter.drawCircle( this.line[0].x, this.line[0].y, painter.toPx(5) ); } } } }