|
@@ -7,6 +7,7 @@ import { mathUtil } from "../Util/MathUtil.js";
|
|
|
import ElementEvents from "../enum/ElementEvents.js";
|
|
|
import { elementService } from "../Service/ElementService.js";
|
|
|
import UIEvents from "@/graphic/enum/UIEvents.js";
|
|
|
+import VectorCategory from "@/graphic/enum/VectorCategory.js";
|
|
|
|
|
|
const imgCache = {};
|
|
|
const help = {
|
|
@@ -30,9 +31,10 @@ const help = {
|
|
|
// item.type === VectorType[geoType] &&
|
|
|
geoId === item.vectorId
|
|
|
) {
|
|
|
- if (Style[attr] && Style[attr][geoType]) {
|
|
|
+ const style = Style[attr][geoType] || Style[attr][item.category]
|
|
|
+ if (Style[attr] && style) {
|
|
|
currentAttr = attr;
|
|
|
- return Style[attr][geoType];
|
|
|
+ return style;
|
|
|
}
|
|
|
}
|
|
|
return prev;
|
|
@@ -40,8 +42,7 @@ const help = {
|
|
|
currentAttr,
|
|
|
];
|
|
|
},
|
|
|
- setVectorStyle(ctx, vector, geoType = vector.geoType) {
|
|
|
- const [styles, attr] = help.getVectorStyle(vector, geoType);
|
|
|
+ setStyle(ctx, styles) {
|
|
|
for (const style in styles) {
|
|
|
if (typeof styles[style] === "function") {
|
|
|
styles[style](ctx, vector);
|
|
@@ -49,6 +50,20 @@ const help = {
|
|
|
ctx[style] = styles[style];
|
|
|
}
|
|
|
}
|
|
|
+ },
|
|
|
+ setVectorStyle(ctx, vector, geoType = vector.geoType) {
|
|
|
+ let styles, attr
|
|
|
+ if (Array.isArray(geoType)) {
|
|
|
+ for (const type of geoType) {
|
|
|
+ [styles, attr] = help.getVectorStyle(vector, type);
|
|
|
+ if (styles) {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ [styles, attr] = help.getVectorStyle(vector, geoType);
|
|
|
+ }
|
|
|
+ help.setStyle(ctx, styles)
|
|
|
return [styles, attr];
|
|
|
},
|
|
|
transformCoves(lines) {
|
|
@@ -99,6 +114,86 @@ const help = {
|
|
|
};
|
|
|
}));
|
|
|
},
|
|
|
+ getTextCenter(ctx, txt) {
|
|
|
+ const text = ctx.measureText(txt);
|
|
|
+ const height = text.actualBoundingBoxAscent + text.actualBoundingBoxDescent
|
|
|
+ return {
|
|
|
+ width: text.width,
|
|
|
+ height,
|
|
|
+ x: text.width / 2,
|
|
|
+ y: -height / 2
|
|
|
+ }
|
|
|
+ },
|
|
|
+ // 绘制圆角矩形
|
|
|
+ roundRect(ctx, x, y, width, height, radius) {
|
|
|
+ ctx.beginPath();
|
|
|
+ ctx.moveTo(x + radius, y);
|
|
|
+ ctx.lineTo(x + width - radius, y);
|
|
|
+ ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
|
|
|
+ ctx.lineTo(x + width, y + height - radius);
|
|
|
+ ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
|
|
|
+ ctx.lineTo(x + radius, y + height);
|
|
|
+ ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
|
|
|
+ ctx.lineTo(x, y + radius);
|
|
|
+ ctx.quadraticCurveTo(x, y, x + radius, y);
|
|
|
+ ctx.closePath();
|
|
|
+ },
|
|
|
+ getRealDistance(p1, p2) {
|
|
|
+ return Math.round(mathUtil.getDistance(p1, p2) * 100) / 100
|
|
|
+ },
|
|
|
+ getPerpendicularPoint(p1, p2, p3, d) {
|
|
|
+ if (p1.x === p2.x) {
|
|
|
+ return {x: p3.x + d, y: p3.y}
|
|
|
+ } else if (p1.y === p2.y) {
|
|
|
+ return {x: p3.x, y: p3.y + d}
|
|
|
+ }
|
|
|
+
|
|
|
+ // 计算通过 p1 和 p2 的直线的斜率和截距
|
|
|
+ const slope = (p2.y - p1.y) / (p2.x - p1.x);
|
|
|
+ const intercept = p1.y - slope * p1.x;
|
|
|
+
|
|
|
+ // 计算垂直线的斜率和截距
|
|
|
+ const perpendicularSlope = -1 / slope;
|
|
|
+ const perpendicularIntercept = p3.y - perpendicularSlope * p3.x;
|
|
|
+
|
|
|
+ // 计算垂足点 p0
|
|
|
+ const x = (perpendicularIntercept - intercept) / (slope - perpendicularSlope);
|
|
|
+ const y = slope * x + intercept;
|
|
|
+ const p0 = { x, y };
|
|
|
+
|
|
|
+// 计算点 p4
|
|
|
+ const distance = d; // 指定距离
|
|
|
+ const dx = distance / Math.sqrt(1 + perpendicularSlope ** 2);
|
|
|
+ const dy = perpendicularSlope * dx;
|
|
|
+ return { x: p0.x + dx, y: p0.y + dy };
|
|
|
+ },
|
|
|
+ drawLineText(ctx, start, end, text, style) {
|
|
|
+ if (start.x > end.x) {
|
|
|
+ [start, end] = [end, start]
|
|
|
+ }
|
|
|
+
|
|
|
+ const angle = Math.atan2(end.y - start.y, end.x - start.x) * 180 / Math.PI;
|
|
|
+ const center = mathUtil.lineCenter(start, end)
|
|
|
+
|
|
|
+ ctx.save();
|
|
|
+ ctx.translate(center.x, center.y);
|
|
|
+ ctx.rotate(angle * Math.PI / 180);
|
|
|
+ const textCenter = help.getTextCenter(ctx, text)
|
|
|
+ const padding = style.padding;
|
|
|
+ help.roundRect(
|
|
|
+ ctx,
|
|
|
+ -textCenter.x - padding,
|
|
|
+ textCenter.y - padding,
|
|
|
+ textCenter.width + 2 * padding,
|
|
|
+ textCenter.height + 2 * padding,
|
|
|
+ (textCenter.height / 2) + padding
|
|
|
+ )
|
|
|
+ ctx.fillStyle = style.backColor
|
|
|
+ ctx.fill()
|
|
|
+ ctx.fillStyle = style.fillColor
|
|
|
+ ctx.fillText(text, -textCenter.x, -textCenter.y);
|
|
|
+ ctx.restore();
|
|
|
+ }
|
|
|
};
|
|
|
|
|
|
export default class Draw {
|
|
@@ -407,13 +502,10 @@ export default class Draw {
|
|
|
|
|
|
ctx.save();
|
|
|
|
|
|
- const [style] = help.setVectorStyle(this.context, vector, "Arrow");
|
|
|
+ const [style] = help.setVectorStyle(this.context, vector);
|
|
|
if (vector.arrowColor) {
|
|
|
ctx.strokeStyle = vector.arrowColor;
|
|
|
}
|
|
|
- ctx.beginPath();
|
|
|
- ctx.moveTo(start.x, start.y);
|
|
|
- ctx.lineTo(end.x, end.y);
|
|
|
|
|
|
const dires =
|
|
|
vector.category === UIEvents.MeasureLine
|
|
@@ -431,7 +523,7 @@ export default class Draw {
|
|
|
ctx.stroke();
|
|
|
ctx.restore();
|
|
|
|
|
|
- if ([Style.Focus.Arrow, Style.Select.Arrow].includes(style)) {
|
|
|
+ if ([Style.Focus.ArrowLine, Style.Select.ArrowLine].includes(style)) {
|
|
|
this.drawPoint(startReal);
|
|
|
this.drawPoint(endReal);
|
|
|
}
|
|
@@ -523,17 +615,29 @@ export default class Draw {
|
|
|
drawPoint(vector) {
|
|
|
const pt = coordinate.getScreenXY({ x: vector.x, y: vector.y });
|
|
|
const ctx = this.context;
|
|
|
- const [style] = help.setVectorStyle(ctx, vector, vector.geoType || "Point");
|
|
|
+ const [style] = help.setVectorStyle(
|
|
|
+ ctx,
|
|
|
+ vector,
|
|
|
+ [vector.category, vector.geoType, "Point"]
|
|
|
+
|
|
|
+ );
|
|
|
if (vector.color) {
|
|
|
ctx.strokeStyle = vector.color;
|
|
|
}
|
|
|
- const radius = help.getReal(vector.radius || style.radius);
|
|
|
- ctx.save();
|
|
|
- ctx.beginPath();
|
|
|
- ctx.arc(pt.x, pt.y, radius, 0, Math.PI * 2, true);
|
|
|
- ctx.stroke();
|
|
|
- ctx.fill();
|
|
|
- ctx.restore();
|
|
|
+ const draw = (style) => {
|
|
|
+ const radius = help.getReal(vector.radius || style.radius);
|
|
|
+ ctx.save();
|
|
|
+ ctx.beginPath();
|
|
|
+ ctx.arc(pt.x, pt.y, radius, 0, Math.PI * 2, true);
|
|
|
+ help.setStyle(ctx, style)
|
|
|
+ ctx.stroke();
|
|
|
+ ctx.fill();
|
|
|
+ ctx.restore();
|
|
|
+ }
|
|
|
+ draw(style)
|
|
|
+ if (style.out) {
|
|
|
+ draw(style.out)
|
|
|
+ }
|
|
|
|
|
|
if (import.meta.env.DEV) {
|
|
|
if (vector.vectorId) {
|
|
@@ -548,9 +652,10 @@ export default class Draw {
|
|
|
setStyle && help.setVectorStyle(ctx, null, "Text");
|
|
|
|
|
|
const pt = coordinate.getScreenXY(position);
|
|
|
- const text = ctx.measureText(txt);
|
|
|
- pt.x -= text.width / 2;
|
|
|
- pt.y += (text.actualBoundingBoxAscent + text.actualBoundingBoxDescent) / 2;
|
|
|
+ const textCenter = help.getTextCenter(ctx, txt);
|
|
|
+ pt.x -= textCenter.x;
|
|
|
+ pt.y -= textCenter.y;
|
|
|
+
|
|
|
if (angle) {
|
|
|
ctx.translate(pt.x, pt.y);
|
|
|
ctx.rotate(angle);
|
|
@@ -578,24 +683,53 @@ export default class Draw {
|
|
|
this.context.font = oldFont;
|
|
|
}
|
|
|
|
|
|
- drawLineText(vector) {
|
|
|
+ drawLineText(vector, style) {
|
|
|
+ const startReal = dataService.getPoint(vector.startId);
|
|
|
+ const endReal = dataService.getPoint(vector.endId);
|
|
|
+ help.drawLineText(
|
|
|
+ this.context,
|
|
|
+ coordinate.getScreenXY(startReal),
|
|
|
+ coordinate.getScreenXY(endReal),
|
|
|
+ help.getRealDistance(startReal, endReal) + "m",
|
|
|
+ style
|
|
|
+ )
|
|
|
+ }
|
|
|
+
|
|
|
+ drawBaseLineLabel(vector) {
|
|
|
const startReal = dataService.getPoint(vector.startId);
|
|
|
const start = coordinate.getScreenXY(startReal);
|
|
|
const endReal = dataService.getPoint(vector.endId);
|
|
|
const end = coordinate.getScreenXY(endReal);
|
|
|
+ const point = mathUtil.translate(
|
|
|
+ end, start, end, mathUtil.getDistance(start, end) / 3
|
|
|
+ )
|
|
|
+ const p4 = help.getPerpendicularPoint(start, end, point, 30)
|
|
|
+ const ctx = this.context
|
|
|
+ ctx.beginPath();
|
|
|
+ const [style] = help.setVectorStyle(
|
|
|
+ this.context,
|
|
|
+ vector,
|
|
|
+ vector.category || vector.geoType
|
|
|
+ );
|
|
|
+ ctx.moveTo(point.x, point.y);
|
|
|
+ ctx.lineTo(p4.x, p4.y);
|
|
|
+ ctx.stroke();
|
|
|
|
|
|
- const angle = Math.atan2(end.y - start.y, end.x - start.x) * 180 / Math.PI;
|
|
|
- this.context.translate(start.x, start.y);
|
|
|
- this.context.rotate(angle * Math.PI / 180);
|
|
|
-
|
|
|
-// 在路径上绘制文本
|
|
|
- context.fillText('Hello World!', 0, 0);
|
|
|
+ const p5 = help.getPerpendicularPoint(start, end, point, 35)
|
|
|
+ help.drawLineText(
|
|
|
+ this.context,
|
|
|
+ help.getPerpendicularPoint(point, p5, p5, 10),
|
|
|
+ help.getPerpendicularPoint(point, p5, p5, -10),
|
|
|
+ "基准线",
|
|
|
+ {
|
|
|
+ padding: 6,
|
|
|
+ backColor: "rgba(0,0,0,0)",
|
|
|
+ fillColor: style.strokeStyle
|
|
|
+ }
|
|
|
+ )
|
|
|
}
|
|
|
|
|
|
drawLine(vector) {
|
|
|
- if ([UIEvents.Arrow, UIEvents.MeasureFree].includes(vector.category)) {
|
|
|
- return this.drawArrow(vector);
|
|
|
- }
|
|
|
const startReal = dataService.getPoint(vector.startId);
|
|
|
const start = coordinate.getScreenXY(startReal);
|
|
|
const endReal = dataService.getPoint(vector.endId);
|
|
@@ -617,10 +751,18 @@ export default class Draw {
|
|
|
this.context.stroke();
|
|
|
this.context.restore();
|
|
|
|
|
|
- // if (attr) {
|
|
|
- // this.drawPoint(startReal)
|
|
|
- // this.drawPoint(endReal)
|
|
|
- // }
|
|
|
+ switch (vector.category) {
|
|
|
+ case VectorCategory.Line.ArrowLine:
|
|
|
+ this.drawArrow(vector);
|
|
|
+ break
|
|
|
+ case VectorCategory.Line.BaseLine:
|
|
|
+ this.drawBaseLineLabel(vector)
|
|
|
+ break;
|
|
|
+ case VectorCategory.Line.MeasureLine:
|
|
|
+ this.drawLineText(vector, style.text)
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
}
|
|
|
|
|
|
drawElementLine(element) {
|