/* * ********************************************************************************************************************* * * !! * .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 { SMatrix, SObject } from "@persagy-web/base"; import { SPaintEngine } from "./engines/SPaintEngine"; import { SBrush, SCanvasPaintEngine, SCanvasView, SFont, SLine, SPath, SPen, SPoint, SRect, SSize } from "./"; import { SCompositeType } from "./enums/SCompositeType"; import { SArrow } from "./types/SArrow"; import { SArrowStyleType } from "./enums/SArrowStyleType"; import { SShadow } from "./SShadow"; /** * Painter * * @author 庞利祥(sybotan@126.com) */ export class SPainter extends SObject { //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // 属性定义 /** 绘制引擎 */ private readonly engine: SPaintEngine; /** 画笔 */ get pen(): SPen { return this.engine.state.pen; } // Get pen set pen(value: SPen) { this.engine.state.pen = value; } // Set pen /** 画刷 */ get brush(): SBrush { return this.engine.state.brush; } // Get brush set brush(value: SBrush) { this.engine.state.brush = value; } // Set brush /** 字体属性 */ get font(): SFont { return this.engine.state.font; } // Get font set font(value: SFont) { this.engine.state.font = value; this.engine.changeFont(value); } // Set font /** 融合属性 */ get composite(): number { return this.engine.state._composite; } // Get composite set composite(value: number) { this.engine.state._composite = value; } // Set composite /** 阴影设置 */ get shadow(): SShadow { return this.engine.state.shadow; } // Get shadow set shadow(value: SShadow) { this.engine.state.shadow = value; } // Set shadow /** 变换矩阵 */ get worldTransform(): SMatrix { return this.engine.state.matrix; } // Get worldTransform /** * 构造函数 * * @param engine 绘制引擎 */ constructor(engine: SCanvasView | SPaintEngine) { super(); if (engine instanceof SCanvasView) { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion this.engine = new SCanvasPaintEngine(engine.canvas!!); } else { this.engine = engine; } this.pen = new SPen(); this.brush = new SBrush(); this.font = new SFont(); this.composite = SCompositeType.SourceOver; this.shadow = new SShadow(); } // Constructor() /** * 保存painter状态 */ save(): void { this.engine.save(); } // Function save() /** * 恢复painter状态 */ restore(): void { this.engine.restore(); } // Function restore() // ================================================================================================================= // 变换相关 /** * 平移变换 * * @param x X轴方向平移 * @param y Y辆方向平移 */ translate(x: number, y: number): void { this.engine.translate(x, y); } // Function translate() /** * 缩放 * * @param x X轴方向缩放 * @param y Y辆方向缩放 */ scale(x: number, y: number): void { this.engine.scale(x, y); } // Function scale() /** * 旋转 * * @param angle 旋转角度(单位弧度) */ rotate(angle: number): void { this.engine.rotate(angle); } // Function rotate() /** * 将当前的变形矩阵乘上一个基于自身参数的矩阵 * * @param m11 水平方向的缩放 * @param m12 水平方向的倾斜偏移 * @param m21 竖直方向的倾斜偏移 * @param m22 竖直方向的缩放 * @param dx 水平方向的移动 * @param dy 竖直方向的移动 */ transform( m11: number, m12: number, m21: number, m22: number, dx: number, dy: number ): void { this.engine.transform(m11, m12, m21, m22, dx, dy); } // Function transform() /** * 将当前的变形矩阵重置为单位矩阵,再将当前的变形矩阵乘上一个基于自身参数的矩阵 * * @param m11 水平方向的缩放 * @param m12 水平方向的倾斜偏移 * @param m21 竖直方向的倾斜偏移 * @param m22 竖直方向的缩放 * @param dx 水平方向的移动 * @param dy 竖直方向的移动 */ setTransform( m11: number, m12: number, m21: number, m22: number, dx: number, dy: number ): void { this.engine.setTransform(m11, m12, m21, m22, dx, dy); } // Function transform() /** * 重置当前变形为单位矩阵。等价于调用setTransform(1, 0, 0, 1, 0, 0) */ resetTransform(): void { this.engine.resetTransform(); } // Function resetTransform() // ================================================================================================================= // 绘制相关 /** * 设置裁剪路径 * * @param path 裁剪路径 */ setClip(path: Path2D): void { this.engine.setClip(path); } // Function setClip() /** * 清空矩形区域 * * @param rect 矩形 */ clearRect(rect: SRect): void; /** * 清空矩形区域 * * @param leftTop 左上角坐标 * @param rightBottom 右下角坐标 */ clearRect(leftTop: SPoint, rightBottom: SPoint): void; /** * 清空矩形区域 * * @param leftTop 左上角坐标 * @param size 大小 */ clearRect(leftTop: SPoint, size: SSize): void; /** * 清空矩形区域 * * @param x X坐标 * @param y Y坐标 * @param w 宽度 * @param h 高度 */ clearRect(x: number, y: number, w: number, h: number): void; /** * 清空矩形区域(重载实现) * * @param x X坐标 | 左上角坐标 | 矩形 * @param y Y坐标 | 右下角坐标 | 大小 * @param w 宽度 * @param h 高度 */ clearRect( x: number | SPoint | SRect, y?: number | SPoint | SSize, w?: number, h?: number ): void { if (x instanceof SRect) { this.engine.clearRect(x); } else if (x instanceof SPoint && y instanceof SPoint) { this.engine.clearRect(new SRect(x, y)); } else if (x instanceof SPoint && y instanceof SSize) { this.engine.clearRect(new SRect(x, y)); } else { this.engine.clearRect( new SRect(x as number, y as number, w as number, h as number) ); } } // Function clearRect() /** * 绘制矩形 * * @param rect 矩形 */ drawRect(rect: SRect): void; /** * 绘制矩形 * * @param leftTop 左上角坐标 * @param rightBottom 右下角坐标 */ drawRect(leftTop: SPoint, rightBottom: SPoint): void; /** * 绘制矩形 * * @param leftTop 左上角坐标 * @param size 大小 */ drawRect(leftTop: SPoint, size: SSize): void; /** * 绘制矩形 * * @param x X坐标 * @param y Y坐标 * @param w 宽度 * @param h 高度 */ drawRect(x: number, y: number, w: number, h: number): void; /** * 绘制矩形 * * @param x X坐标 | 左上角坐标 | 矩形 * @param y Y坐标 | 右下角坐标 | 大小 * @param w 宽度 * @param h 高度 */ drawRect( x: number | SPoint | SRect, y?: number | SPoint | SSize, w?: number, h?: number ): void { if (x instanceof SRect) { this.engine.drawRect(x); } else if (x instanceof SPoint && y instanceof SPoint) { this.engine.drawRect(new SRect(x, y)); } else if (x instanceof SPoint && y instanceof SSize) { this.engine.drawRect(new SRect(x, y)); } else { this.engine.drawRect( new SRect(x as number, y as number, w as number, h as number) ); } } // Function drawRect() /** * 绘制带导角矩形 * * @param rect 矩形 * @param r 导角半径 */ drawRoundRect(rect: SRect, r: number): void; /** * 绘制带导角矩形 * * @param pos 左上角位置 * @param size 大小 * @param r 导角半径 */ drawRoundRect(pos: SPoint, size: SSize, r: number): void; /** * 绘制带导角矩形 * * @param x X坐标 * @param y Y坐标 * @param w 宽度 * @param h 高度 * @param r 导角半径 */ drawRoundRect(x: number, y: number, w: number, h: number, r: number): void; /** * 绘制带导角矩形 * * @param x X坐标 * @param y Y坐标 * @param w 宽度 * @param h 高度 * @param radius 导角半径 */ drawRoundRect( x: number | SPoint | SRect, y: number | SSize, w?: number, h?: number, radius?: number ): void { let rect: SRect | null; let r: number | null; if (x instanceof SRect) { rect = x; r = y as number; } else if (x instanceof SPoint && y instanceof SSize) { rect = new SRect(x, y); r = w as number; } else { rect = new SRect( x as number, y as number, w as number, h as number ); r = radius as number; } if (rect != null && r != null) { this.engine.drawRoundRect(rect, r); } } // Function drawRoundRect() /** * 绘制圆形 * * @param cx 圆心X坐标 * @param cy 圆心X坐标 * @param r 圆半径 */ drawCircle(cx: number, cy: number, r: number): void { this.engine.drawCircle(cx, cy, r); } // Function drawCircle() /** * 绘制椭圆 * * @param cx 圆点X坐标 * @param cy 圆点Y坐标 * @param rx 水平半径 * @param ry 垂直半径 */ drawEllipse(cx: number, cy: number, rx: number, ry: number): void { this.engine.drawEllipse(cx, cy, rx, ry); } // Function drawEllipse() /** * 绘制一条线段 * * @param line 线段 */ drawLine(line: SLine): void; /** * 绘制一条线段 * * @param p1 启点坐标 * @param p2 终点坐标 */ drawLine(p1: SPoint, p2: SPoint): void; /** * 绘制一条线段 * * @param x1 启点X坐标 * @param y1 启点Y坐标 * @param x2 终点X坐标 * @param y2 终点Y坐标 */ drawLine(x1: number, y1: number, x2: number, y2: number): void; /** * 绘制一条线段(重载实现) * * @param x1 启点X坐标 | 起点1坐标 | 线段 | * @param y1 启点Y坐标 | 终点坐标 * @param x2 终点X坐标 * @param y2 终点Y坐标 */ drawLine( x1: number | SPoint | SLine, y1?: number | SPoint, x2?: number, y2?: number ): void { if (x1 instanceof SLine) { this.engine.drawLine(x1); } else if (x1 instanceof SPoint && y1 instanceof SPoint) { this.engine.drawLine(new SLine(x1, y1)); } else { this.engine.drawLine( new SLine( x1 as number, y1 as number, x2 as number, y2 as number ) ); } } // Function drawLine() /** * 绘制折线 * * @param points 折线折点 */ drawPolyline(points: SPoint[]): void { this.engine.drawPolyline(points); } // Function drawPolyline() /** * 绘制多边形 * * @param points 多边形顶点 */ drawPolygon(points: SPoint[]): void { this.engine.drawPolygon(points); } // Function drawPolygon() /** * 绘制路径 * * @param path 路径 */ drawPath(path: SPath): void { this.engine.drawPath(path); } // Function drawPath() /** * 绘制文本 * * @param text 文本内容 * @param x X坐标 * @param y Y坐标 * @param maxWidth 最大宽度 */ drawText(text: string, x: number, y: number, maxWidth?: number): void { this.engine.drawText(text, x, y, maxWidth); } // Function drawText() /** * 绘制图片 * * @param img 图片 * @param x X坐标 * @param y Y坐标 * @param width 宽度 * @param height 高度 */ drawImage( img: CanvasImageSource, x: number, y: number, width?: number, height?: number ): void { this.engine.drawImage(img, x, y, width, height); } // Function drawImage() /** * painter转实现view象素 * * @param p 绘制坐标 */ toPx(p: number): number { return p / this.engine.state.matrix.a; } // Function painterToView() /** * 预测量文本宽度 * * @param text 文本字符 * @return 文本所占长度像素 * */ textWidth(text: string): number { return this.engine.textWidth(text); } /** * 绘制带箭头的线段 * * @param line 线段 * @param style 末端样式 */ drawArrowLine(line: SLine, style?: SArrow): void; /** * 绘制带箭头的线段 * * @param p1 启点坐标 * @param p2 终点坐标 * @param style 末端样式 */ drawArrowLine(p1: SPoint, p2: SPoint, style?: SArrow): void; /** * 绘制带箭头的线段 * * @param x1 启点X坐标 * @param y1 启点Y坐标 * @param x2 终点X坐标 * @param y2 终点Y坐标 * @param style 末端样式 */ drawArrowLine( x1: number, y1: number, x2: number, y2: number, style?: SArrow ): void; /** * 绘制带箭头的线段 * * @param x1 启点X坐标 * @param y1 启点Y坐标 * @param x2 终点X坐标 * @param y2 终点Y坐标 * @param st 线段两端样式 */ drawArrowLine( x1: number | SPoint | SLine, y1?: number | SPoint | SArrow, x2?: number | SArrow, y2?: number, st?: SArrow ): void { let line: SLine, style: SArrow; if (x1 instanceof SLine) { line = x1; style = y1 as SArrow; } else if (x1 instanceof SPoint && y1 instanceof SPoint) { line = new SLine(x1, y1); style = x2 as SArrow; } else { line = new SLine( x1 as number, y1 as number, x2 as number, y2 as number ); style = st as SArrow; } this.engine.drawLine(line); if (style) { if (style.begin) { if (style.begin == SArrowStyleType.Basic) { this.drawBasicArrow(line, false); } else if (style.begin == SArrowStyleType.Triangle) { this.drawTriangleArrow(line, false); } else if (style.begin == SArrowStyleType.Diamond) { this.drawDiamondArrow(line, false); } else if (style.begin == SArrowStyleType.Square) { this.drawSquareArrow(line, false); } else if (style.begin == SArrowStyleType.Circle) { this.drawCircleArrow(line, false); } } if (style.end) { if (style.end == SArrowStyleType.Basic) { this.drawBasicArrow(line, true); } else if (style.end == SArrowStyleType.Triangle) { this.drawTriangleArrow(line, true); } else if (style.end == SArrowStyleType.Diamond) { this.drawDiamondArrow(line, true); } else if (style.end == SArrowStyleType.Square) { this.drawSquareArrow(line, true); } else if (style.end == SArrowStyleType.Circle) { this.drawCircleArrow(line, true); } } } } // Function drawArrowLine() /** * 私有计算方法-绘制线段末端标准箭头 * * @param line 要加末端样式的线段 * @param isEnd 是否为结束位置 * */ private drawBasicArrow(line: SLine, isEnd: boolean = true): void { // 定义箭头长度 const d = this.toPx(10); // 箭头横坐标 const x1 = d * Math.cos(Math.PI / 4); // 箭头纵坐标 const y1 = d * Math.sin(Math.PI / 4); // 线段与x轴夹角 const fo = Math.atan(line.dy / line.dx); this.save(); // 是否为终点画箭头 if (isEnd) { this.translate(line.x2, line.y2); this.rotate((fo * 180) / Math.PI); this.engine.drawPolyline([ new SPoint(-x1, y1), new SPoint(0, 0), new SPoint(-x1, -y1) ]); } else { this.translate(line.x1, line.y1); this.rotate((fo * 180) / Math.PI); this.engine.drawPolyline([ new SPoint(x1, y1), new SPoint(0, 0), new SPoint(x1, -y1) ]); } this.restore(); } // Function drawArrow() /** * 私有计算方法-绘制线段末端三角形箭头 * * @param line 要加末端样式的线段 * @param isEnd 是否为结束位置 * */ private drawTriangleArrow(line: SLine, isEnd: boolean = true): void { // 定义箭头长度 const d = this.toPx(10); // 箭头横坐标 const x1 = d * Math.cos(Math.PI / 12); // 箭头纵坐标 const y1 = d * Math.sin(Math.PI / 12); // 线段与x轴夹角 const fo = Math.atan(line.dy / line.dx); this.save(); // 是否为终点画箭头 if (isEnd) { this.translate(line.x2, line.y2); this.rotate((fo * 180) / Math.PI); this.engine.drawPolygon([ new SPoint(-x1, y1), new SPoint(0, 0), new SPoint(-x1, -y1) ]); } else { this.translate(line.x1, line.y1); this.rotate((fo * 180) / Math.PI); this.engine.drawPolygon([ new SPoint(x1, y1), new SPoint(0, 0), new SPoint(x1, -y1) ]); } this.restore(); } // Function drawTriangleArrow() /** * 私有计算方法-绘制线段末端菱形箭头 * * @param line 要加末端样式的线段 * @param isEnd 是否为结束位置 * */ private drawDiamondArrow(line: SLine, isEnd: boolean = true): void { // 定义箭头长度 const d = this.toPx(5); // 箭头横坐标 const x1 = d * Math.cos(Math.PI / 4); // 箭头纵坐标 const y1 = d * Math.sin(Math.PI / 4); // 线段与x轴夹角 const fo = Math.atan(line.dy / line.dx); this.save(); // 是否为终点画箭头 if (isEnd) { this.translate(line.x2, line.y2); this.rotate((fo * 180) / Math.PI); this.engine.drawPolygon([ new SPoint(-x1, y1), new SPoint(0, 0), new SPoint(-x1, -y1), new SPoint(-Math.sqrt(2) * d, 0) ]); } else { this.translate(line.x1, line.y1); this.rotate((fo * 180) / Math.PI); this.engine.drawPolygon([ new SPoint(x1, y1), new SPoint(0, 0), new SPoint(x1, -y1), new SPoint(Math.sqrt(2) * d, 0) ]); } this.restore(); } // Function drawDiamondArrow() /** * 私有计算方法-绘制线段末端方形箭头 * * @param line 要加末端样式的线段 * @param isEnd 是否为结束位置 * */ private drawSquareArrow(line: SLine, isEnd: boolean = true): void { // 定义箭头长度 const d = this.toPx(5); // 线段与x轴夹角 const fo = Math.atan(line.dy / line.dx); this.save(); // 是否为终点画箭头 if (isEnd) { this.translate(line.x2, line.y2); this.rotate((fo * 180) / Math.PI); this.engine.drawPolygon([ new SPoint(-d, d / 2), new SPoint(0, d / 2), new SPoint(0, -d / 2), new SPoint(-d, -d / 2) ]); } else { this.translate(line.x1, line.y1); this.rotate((fo * 180) / Math.PI); this.engine.drawPolygon([ new SPoint(0, d / 2), new SPoint(d, d / 2), new SPoint(d, -d / 2), new SPoint(0, -d / 2) ]); } this.restore(); } // Function drawSquareArrow() /** * 私有计算方法-绘制线段末端圆形箭头 * * @param line 要加末端样式的线段 * @param isEnd 是否为结束位置 * */ private drawCircleArrow(line: SLine, isEnd: boolean = true): void { // 定义箭头长度 const d = this.toPx(5); // 线段与x轴夹角 const fo = Math.atan(line.dy / line.dx); // 是否为终点画箭头 this.save(); if (isEnd) { this.translate(line.x2, line.y2); this.rotate((fo * 180) / Math.PI); this.engine.drawCircle(-d / 2, 0, d / 2); } else { this.translate(line.x1, line.y1); this.rotate((fo * 180) / Math.PI); this.engine.drawCircle(d / 2, 0, d / 2); } this.restore(); } // Function drawCircleArrow() } // Class SPainter