import { WorkingAreaComponent } from '../working-area.component'; import * as PIXI from 'pixi.js'; import { AxShape } from './axShape'; import { Sprite } from 'pixi.js'; import { GameMode } from './gameMode'; /** * 连接箭头 */ export class AxArrowConnector extends AxShape { pointSprites: Array = new Array(); line: PIXI.Graphics; text: PIXI.Text; style = new PIXI.TextStyle({ fontFamily: 'Arial', fontSize: 18, fontStyle: 'normal', fontWeight: 'bold', fill: ['#000000'], stroke: '#ffffff', strokeThickness: 3, dropShadow: true, dropShadowColor: '#000000', dropShadowBlur: 3, dropShadowAngle: Math.PI / 6, dropShadowDistance: 1, wordWrap: false, wordWrapWidth: 100, }); pts: PIXI.Point[]; markerStart = true;// 是否绘制起始箭头 markerEnd = true;// 是否绘制结束箭头 constructor(assetData: any, workingArea: WorkingAreaComponent,markerStart: boolean,markerEnd:boolean) { super(assetData, workingArea); this.markerStart = markerStart; this.markerEnd = markerEnd; this.name = assetData.Id; this.text = new PIXI.Text(this.assetData.Name + '\r\n' + this.assetData.PropertyInfos?.find((item: { PropertyName: string; }) => item.PropertyName === '名称/编号')?.PropertyValue, this.style); this.line = new PIXI.Graphics(); this.addChild(this.text); this.addChild(this.line); this.workingArea.backgroundImage.addChild(this); this.refresh(); this.drawPoints(); this.sortableChildren = true; this.text.zIndex = this.children.length; this.text.visible = this.showName; this.text.angle = -this.workingArea.backgroundImage.angle; } public drawPoints() { this.assetData.MultiPoint.forEach(element => { var point = new Sprite(this.pointTexture); point.position = element; point.anchor.set(0.5); this.pointSprites.push(point); this.addChild(point); }); this.pointSprites.forEach((value, index, array) => { value.interactive = true; value .on('pointerdown', event => { event.stopPropagation(); if (this.workingArea.allowEdit && this.assetData.GameMode === this.workingArea.canvasData.gameMode) { event.currentTarget.data = event.data; event.currentTarget.alpha = 0.5; event.currentTarget.dragging = true; } }) .on('pointerup', event => { if (event.currentTarget.dragging) { event.currentTarget.alpha = 1; event.currentTarget.dragging = false; event.currentTarget.data = null; } }) .on('pointerupoutside', event => { if (event.currentTarget.dragging) { event.currentTarget.alpha = 1; event.currentTarget.dragging = false; event.currentTarget.data = null; } }) .on('pointermove', event => { if (event.currentTarget.dragging) { const newPosition = event.currentTarget.data.getLocalPosition(event.currentTarget.parent); event.currentTarget.x = newPosition.x; event.currentTarget.y = newPosition.y; this.assetData.MultiPoint[index].x = newPosition.x; this.assetData.MultiPoint[index].y = newPosition.y; this.workingArea.canvasData.isChange = true; this.refresh(); this.drawBorder(1 / this.workingArea.backgroundImage.scale.x); } }) .on('rightclick', event => { }); }) this.setPointVisiable(false); } /** * 设置点显示状态 * @param b true/false */ public setPointVisiable(b:boolean) { this.pointSprites.forEach(item => { item.visible = b; }) } // 设置缩放 public setItemScale(scale: number) { // this.text.scale.set(scale); this.pointSprites.forEach(point => { point.scale.set(scale); }); } public setNameVisible(value: boolean, mode: GameMode) { if (this.assetData.GameMode === mode) { this.text.visible = value; } } /** * 刷新形状 */ public refresh(): void { const c = this.line; const pts = this.assetData.MultiPoint; if (pts.length < 2) { return; } this.text.position = this.getLineCenter(pts[0], pts[1]); this.text.anchor.set(0.5); this.text.text = this.assetData.Name + '\r\n' + this.assetData.PropertyInfos?.find(item => item.PropertyName === '名称/编号')?.PropertyValue; const strokeWidth = 1; const startWidth = 30 + strokeWidth; const endWidth = 30 + strokeWidth; const edgeWidth = this.assetData.Thickness === 0 ? 10 : this.assetData.Thickness; // 宽度 const openEnded = false; const spacing = (openEnded) ? 0 : 0 + strokeWidth / 2; const startSize = 30 + strokeWidth; const endSize = 30 + strokeWidth; const isRounded = true; const lineColor = 0x000000; const fillColor: number = this.assetData.Color.substring(0, 7).replace('#', '0x');; const pe = pts[pts.length - 1]; let i0 = 1; while (i0 < pts.length - 1 && pts[i0].x === pts[0].x && pts[i0].y === pts[0].y) { i0++; } const dx = pts[i0].x - pts[0].x; const dy = pts[i0].y - pts[0].y; const dist = Math.sqrt(dx * dx + dy * dy); if (dist === 0) { return; } let nx = dx / dist; let nx1 = nx; let nx2 = nx; let ny = dy / dist; let ny2 = ny; let ny1 = ny; let orthx = edgeWidth * ny; let orthy = -edgeWidth * nx; const fns = []; // if (isRounded) { // // c.setLineJoin('round'); // c.lineTextureStyle({ join: PIXI.LINE_JOIN.ROUND }); // } else if (pts.length > 2) { // // Only mitre if there are waypoints // // c.setMiterLimit(1.42); // c.lineTextureStyle({ miterLimit: 1.42 }); // } // c.lineStyle(1, 0x000000, 1); c.clear(); c.lineTextureStyle({ width: 1, color: lineColor, join: PIXI.LINE_JOIN.ROUND }); const startNx = nx; const startNy = ny; if (!openEnded) { c.beginFill(fillColor); } if (this.markerStart && !openEnded) { this.paintMarker(c, pts[0].x, pts[0].y, nx, ny, startSize, startWidth, edgeWidth, spacing, true); } else { const outStartX = pts[0].x + orthx / 2 + spacing * nx; const outStartY = pts[0].y + orthy / 2 + spacing * ny; const inEndX = pts[0].x - orthx / 2 + spacing * nx; const inEndY = pts[0].y - orthy / 2 + spacing * ny; if (openEnded) { c.moveTo(outStartX, outStartY); fns.push( () => { c.lineTo(inEndX, inEndY); }); } else { c.moveTo(inEndX, inEndY); c.lineTo(outStartX, outStartY); } } let dx1 = 0; let dy1 = 0; let dist1 = 0; for (let i = 0; i < pts.length - 2; i++) { const pos = this.relativeCcw(pts[i].x, pts[i].y, pts[i + 1].x, pts[i + 1].y, pts[i + 2].x, pts[i + 2].y); dx1 = pts[i + 2].x - pts[i + 1].x; dy1 = pts[i + 2].y - pts[i + 1].y; dist1 = Math.sqrt(dx1 * dx1 + dy1 * dy1); if (dist1 !== 0) { nx1 = dx1 / dist1; ny1 = dy1 / dist1; const tmp1 = nx * nx1 + ny * ny1; const tmp = Math.max(Math.sqrt((tmp1 + 1) / 2), 0.04); nx2 = (nx + nx1); ny2 = (ny + ny1); const dist2 = Math.sqrt(nx2 * nx2 + ny2 * ny2); if (dist2 !== 0) { nx2 = nx2 / dist2; ny2 = ny2 / dist2; const strokeWidthFactor = Math.max(tmp, Math.min(1 / 200 + 0.04, 0.35)); const angleFactor = (pos !== 0 && isRounded) ? Math.max(0.1, strokeWidthFactor) : Math.max(tmp, 0.06); const outX = pts[i + 1].x + ny2 * edgeWidth / 2 / angleFactor; const outY = pts[i + 1].y - nx2 * edgeWidth / 2 / angleFactor; const inX = pts[i + 1].x - ny2 * edgeWidth / 2 / angleFactor; const inY = pts[i + 1].y + nx2 * edgeWidth / 2 / angleFactor; if (pos === 0 || !isRounded) { c.lineTo(outX, outY); ((x, y) => { fns.push(() => { c.lineTo(x, y); }); })(inX, inY); } else if (pos === -1) { const c1x = inX + ny * edgeWidth; const c1y = inY - nx * edgeWidth; const c2x = inX + ny1 * edgeWidth; const c2y = inY - nx1 * edgeWidth; c.lineTo(c1x, c1y); if (isRounded) { c.quadraticCurveTo(outX, outY, c2x, c2y); // 圆角 } else { c.lineTo(outX, outY); } ((x, y) => { fns.push(() => { c.lineTo(x, y); }); })(inX, inY); } else { c.lineTo(outX, outY); ((x, y) => { const c1x = outX - ny * edgeWidth; const c1y = outY + nx * edgeWidth; const c2x = outX - ny1 * edgeWidth; const c2y = outY + nx1 * edgeWidth; fns.push(() => { if (isRounded) { c.quadraticCurveTo(x, y, c1x, c1y); } else { c.lineTo(x, y); } }); fns.push(() => { c.lineTo(c2x, c2y); }); })(inX, inY); } nx = nx1; ny = ny1; } } } orthx = edgeWidth * ny1; orthy = - edgeWidth * nx1; if (this.markerEnd && !openEnded) { this.paintMarker(c, pe.x, pe.y, -nx, -ny, endSize, endWidth, edgeWidth, spacing, false); } else { c.lineTo(pe.x - spacing * nx1 + orthx / 2, pe.y - spacing * ny1 + orthy / 2); const inStartX = pe.x - spacing * nx1 - orthx / 2; const inStartY = pe.y - spacing * ny1 - orthy / 2; if (!openEnded) { c.lineTo(inStartX, inStartY); } else { c.moveTo(inStartX, inStartY); fns.splice(0, 0, () => { c.moveTo(inStartX, inStartY); }); } } for (let i = fns.length - 1; i >= 0; i--) { fns[i](); } if (openEnded) { c.closePath(); } else { c.closePath(); c.endFill(); } // c.setShadow(false); // c.setMiterLimit(4); // if (isRounded) // { // c.setLineJoin('flat'); // } // if (pts.length > 2) // { // c.setMiterLimit(4); // if (markerStart && !openEnded) // { // c.begin(); // this.paintMarker(c, pts[0].x, pts[0].y, startNx, startNy, startSize, startWidth, edgeWidth, spacing, true); // c.stroke(); // c.end(); // } // if (markerEnd && !openEnded) // { // c.begin(); // this.paintMarker(c, pe.x, pe.y, -nx, -ny, endSize, endWidth, edgeWidth, spacing, true); // c.stroke(); // c.end(); // } // } this.text.angle = -this.workingArea.backgroundImage.angle; } paintMarker(c: PIXI.Graphics, ptX: number, ptY: number, nx: number, ny: number, size: number, arrowWidth: number, edgeWidth: number, spacing: number, initialMove: boolean) { const widthArrowRatio = edgeWidth / arrowWidth; const orthx = edgeWidth * ny / 2; const orthy = -edgeWidth * nx / 2; const spaceX = (spacing + size) * nx; const spaceY = (spacing + size) * ny; if (initialMove) { c.moveTo(ptX - orthx + spaceX, ptY - orthy + spaceY); } else { c.lineTo(ptX - orthx + spaceX, ptY - orthy + spaceY); } c.lineTo(ptX - orthx / widthArrowRatio + spaceX, ptY - orthy / widthArrowRatio + spaceY); c.lineTo(ptX + spacing * nx, ptY + spacing * ny); c.lineTo(ptX + orthx / widthArrowRatio + spaceX, ptY + orthy / widthArrowRatio + spaceY); c.lineTo(ptX + orthx + spaceX, ptY + orthy + spaceY); } relativeCcw(x1: number, y1: number, x2: number, y2: number, px: number, py: number) { x2 -= x1; y2 -= y1; px -= x1; py -= y1; let ccw = px * y2 - py * x2; if (ccw === 0.0) { ccw = px * x2 + py * y2; if (ccw > 0.0) { px -= x2; py -= y2; ccw = px * x2 + py * y2; if (ccw < 0.0) { ccw = 0.0; } } } return (ccw < 0.0) ? -1 : ((ccw > 0.0) ? 1 : 0); } redraw(): void{ this.pointSprites.forEach(item => { item.destroy(); }) this.pointSprites.splice(0, this.pointSprites.length); this.refresh(); this.drawPoints(); } }