diff --git a/src/pages/measure/components/MeasureAction.tsx b/src/pages/measure/components/MeasureAction.tsx
index 614568b..e809712 100644
--- a/src/pages/measure/components/MeasureAction.tsx
+++ b/src/pages/measure/components/MeasureAction.tsx
@@ -1,9 +1,10 @@
import { Button, Checkbox, CheckboxProps, Radio, RadioChangeEvent, message } from "antd";
import { useState, useEffect } from "react";
import { useNavigate } from "react-router";
-import SectionalView from "./SectionalView";
import { startMeasurement } from "../../../services/measure/analysis"
import { createWebSocket, sharedWsUrl } from "../../../services/socket";
+import GridLayer from "./graph/GridLayer";
+import StandardLayer from "./graph/StandardLayer";
export default function MeasureAction() {
const navigate = useNavigate();
@@ -41,8 +42,11 @@ export default function MeasureAction() {
}
return (
-
-
+
测量步骤
diff --git a/src/pages/measure/components/SectionalView.tsx b/src/pages/measure/components/SectionalView.tsx
deleted file mode 100644
index eb03704..0000000
--- a/src/pages/measure/components/SectionalView.tsx
+++ /dev/null
@@ -1,72 +0,0 @@
-import { useCallback, useEffect, useRef } from "react";
-
-const primaryLineColor = "rgb(203,213,245)";
-const subLineColor = "rgb(226,231,232)";
-
-export default function SectionalView(props: {
- width: number;
- height: number;
- leftPadding: number;
- rightPadding: number;
- topPadding: number;
- bottomPadding: number;
- columns: number;
- rows: number;
- cellNum: number;
-}) {
- const xStartPx = props.leftPadding;
- const xEndPx = props.width - props.rightPadding;
- const xStepPx = (props.width - props.leftPadding - props.rightPadding) / props.columns;
- const xUnitPx = xStepPx / props.cellNum;
- const yStartPx = props.topPadding;
- const yEndPx = props.height - props.bottomPadding;
- const yStepPx = (props.height - props.topPadding - props.bottomPadding) / props.rows;
- const yUnitPx = yStepPx / props.cellNum;
-
- const canvasRef = useRef
(null);
- const drawGrid = useCallback((ctx: CanvasRenderingContext2D) => {
- ctx.beginPath();
- ctx.strokeStyle = subLineColor;
- for (let i = 1; i < props.columns * props.cellNum; ++i) {
- if (i % props.cellNum === 0) continue;
- ctx.moveTo(xStartPx + xUnitPx * i, yStartPx);
- ctx.lineTo(xStartPx + xUnitPx * i, yEndPx);
- }
- for (let j = 1; j < props.rows * props.cellNum; ++j) {
- if (j % props.cellNum === 0) continue;
- ctx.moveTo(xStartPx, yStartPx + yUnitPx * j);
- ctx.lineTo(xEndPx, yStartPx + yUnitPx * j);
- }
- ctx.stroke();
-
- ctx.beginPath();
- ctx.strokeStyle = primaryLineColor;
- for (let i = 0; i <= props.columns; ++i) {
- ctx.moveTo(xStartPx + xStepPx * i, yStartPx);
- ctx.lineTo(xStartPx + xStepPx * i, yEndPx);
- }
- for (let j = 0; j <= props.rows; ++j) {
- ctx.moveTo(xStartPx, yStartPx + yStepPx * j);
- ctx.lineTo(xEndPx, yStartPx + yStepPx * j);
- }
- ctx.stroke();
- },[props.cellNum, props.columns, props.rows, xEndPx, xStartPx, xStepPx, xUnitPx, yEndPx, yStartPx, yStepPx, yUnitPx])
-
- useEffect(() => {
- // 获取canvas的2D绘图上下文
- const canvas = canvasRef.current;
- if (!canvas) return;
-
- const context = canvas.getContext("2d");
- if (!context) return;
- // 使用context对象进行绘图
- drawGrid(context);
- }, [drawGrid]);
-
-
- return (
-
-
-
- );
-}
diff --git a/src/pages/measure/components/graph/AreaLayer.tsx b/src/pages/measure/components/graph/AreaLayer.tsx
new file mode 100644
index 0000000..f7bcede
--- /dev/null
+++ b/src/pages/measure/components/graph/AreaLayer.tsx
@@ -0,0 +1,31 @@
+import { useCallback, useEffect, useRef } from "react";
+
+export default function AreaLayer(props: {
+ width: number;
+ height: number;
+ leftPadding: number;
+ rightPadding: number;
+ topPadding: number;
+ bottomPadding: number;
+}) {
+ const canvasRef = useRef(null);
+ const draw = useCallback((ctx: CanvasRenderingContext2D) => {
+ // 使用context对象进行绘图
+ ctx.fillStyle = "skyblue"; // 设置填充颜色
+ ctx.fillRect(50, 50, 150, 100); // 绘制一个矩形
+ }, []);
+ useEffect(() => {
+ const canvas = canvasRef.current;
+ if (!canvas) return;
+
+ const context = canvas.getContext("2d");
+ if (!context) return;
+ draw(context);
+ }, [draw]);
+
+ return (
+
+
+
+ );
+}
diff --git a/src/pages/measure/components/graph/GridLayer.tsx b/src/pages/measure/components/graph/GridLayer.tsx
new file mode 100644
index 0000000..fb36c71
--- /dev/null
+++ b/src/pages/measure/components/graph/GridLayer.tsx
@@ -0,0 +1,88 @@
+import { useCallback, useEffect, useRef } from "react";
+
+const primaryLineColor = "rgb(203,213,245)";
+const subLineColor = "rgb(226,231,232)";
+
+export default function GridLayer(props: {
+ width: number;
+ height: number;
+ leftPadding: number;
+ rightPadding: number;
+ topPadding: number;
+ bottomPadding: number;
+ columns: number;
+ rows: number;
+ colCellNum: number;
+ rowCellNum: number;
+}) {
+ const xStartPx = props.leftPadding;
+ const xEndPx = props.width - props.rightPadding;
+ const xStepPx = (props.width - props.leftPadding - props.rightPadding) / props.columns;
+ const xUnitPx = xStepPx / props.colCellNum;
+ const yStartPx = props.topPadding;
+ const yEndPx = props.height - props.bottomPadding;
+ const yStepPx = (props.height - props.topPadding - props.bottomPadding) / props.rows;
+ const yUnitPx = yStepPx / props.rowCellNum;
+
+ const canvasRef = useRef(null);
+ const drawGrid = useCallback(
+ (ctx: CanvasRenderingContext2D) => {
+ ctx.beginPath();
+ ctx.strokeStyle = subLineColor;
+ for (let i = 1; i < props.columns * props.colCellNum; ++i) {
+ if (i % props.colCellNum === 0) continue;
+ ctx.moveTo(xStartPx + xUnitPx * i, yStartPx);
+ ctx.lineTo(xStartPx + xUnitPx * i, yEndPx);
+ }
+ for (let j = 1; j < props.rows * props.rowCellNum; ++j) {
+ if (j % props.rowCellNum === 0) continue;
+ ctx.moveTo(xStartPx, yStartPx + yUnitPx * j);
+ ctx.lineTo(xEndPx, yStartPx + yUnitPx * j);
+ }
+ ctx.stroke();
+
+ ctx.beginPath();
+ ctx.strokeStyle = primaryLineColor;
+ for (let i = 0; i <= props.columns; ++i) {
+ ctx.moveTo(xStartPx + xStepPx * i, yStartPx);
+ ctx.lineTo(xStartPx + xStepPx * i, yEndPx);
+ }
+ for (let j = 0; j <= props.rows; ++j) {
+ ctx.moveTo(xStartPx, yStartPx + yStepPx * j);
+ ctx.lineTo(xEndPx, yStartPx + yStepPx * j);
+ }
+ ctx.stroke();
+ },
+ [
+ props.colCellNum,
+ props.columns,
+ props.rowCellNum,
+ props.rows,
+ xEndPx,
+ xStartPx,
+ xStepPx,
+ xUnitPx,
+ yEndPx,
+ yStartPx,
+ yStepPx,
+ yUnitPx,
+ ]
+ );
+
+ useEffect(() => {
+ // 获取canvas的2D绘图上下文
+ const canvas = canvasRef.current;
+ if (!canvas) return;
+
+ const context = canvas.getContext("2d");
+ if (!context) return;
+ // 使用context对象进行绘图
+ drawGrid(context);
+ }, [drawGrid]);
+
+ return (
+
+
+
+ );
+}
diff --git a/src/pages/measure/components/graph/MarkLayer.tsx b/src/pages/measure/components/graph/MarkLayer.tsx
new file mode 100644
index 0000000..28bb3d8
--- /dev/null
+++ b/src/pages/measure/components/graph/MarkLayer.tsx
@@ -0,0 +1,31 @@
+import { useCallback, useEffect, useRef } from "react";
+
+export default function MarkLayer(props: {
+ width: number;
+ height: number;
+ leftPadding: number;
+ rightPadding: number;
+ topPadding: number;
+ bottomPadding: number;
+}) {
+ const canvasRef = useRef(null);
+ const draw = useCallback((ctx: CanvasRenderingContext2D) => {
+ // 使用context对象进行绘图
+ ctx.fillStyle = "skyblue"; // 设置填充颜色
+ ctx.fillRect(50, 50, 150, 100); // 绘制一个矩形
+ }, []);
+ useEffect(() => {
+ const canvas = canvasRef.current;
+ if (!canvas) return;
+
+ const context = canvas.getContext("2d");
+ if (!context) return;
+ draw(context);
+ }, [draw]);
+
+ return (
+
+
+
+ );
+}
diff --git a/src/pages/measure/components/graph/RealtimeLayer.tsx b/src/pages/measure/components/graph/RealtimeLayer.tsx
new file mode 100644
index 0000000..4d4c794
--- /dev/null
+++ b/src/pages/measure/components/graph/RealtimeLayer.tsx
@@ -0,0 +1,31 @@
+import { useCallback, useEffect, useRef } from "react";
+
+export default function RealtimeLayer(props: {
+ width: number;
+ height: number;
+ leftPadding: number;
+ rightPadding: number;
+ topPadding: number;
+ bottomPadding: number;
+}) {
+ const canvasRef = useRef(null);
+ const draw = useCallback((ctx: CanvasRenderingContext2D) => {
+ // 使用context对象进行绘图
+ ctx.fillStyle = "skyblue"; // 设置填充颜色
+ ctx.fillRect(50, 50, 150, 100); // 绘制一个矩形
+ }, []);
+ useEffect(() => {
+ const canvas = canvasRef.current;
+ if (!canvas) return;
+
+ const context = canvas.getContext("2d");
+ if (!context) return;
+ draw(context);
+ }, [draw]);
+
+ return (
+
+
+
+ );
+}
diff --git a/src/pages/measure/components/graph/StandardLayer.tsx b/src/pages/measure/components/graph/StandardLayer.tsx
new file mode 100644
index 0000000..d6bed8f
--- /dev/null
+++ b/src/pages/measure/components/graph/StandardLayer.tsx
@@ -0,0 +1,209 @@
+import { useCallback, useEffect, useRef } from "react";
+import { calculateCircleAbove, calculateCircleBelow } from "../../../../utils";
+
+const unitPx = 8;
+
+const pointsR: [number, number][] = [
+ [0, 0],
+ [9.949007022412, 0.1650166186941],
+ [25.35, 2.184814802617],
+ [35.4, 14.20034968551],
+ [36.31679456414, 32.538841443],
+ [32.90417417089, 37.53190928715],
+ [20.0, 41.8333134842],
+];
+const pointsL: [number, number][] = [
+ [-20.0, 41.83331348425],
+ [-32.90417417089, 37.53190928715],
+ [-36.31679456414, 32.538841443],
+ [-35.4, 14.20034968551],
+ [-25.35, 2.184814802617],
+ [-9.949007022412, 0.1650166186941],
+ [0, 0],
+];
+const pointsRight = pointsR.map(p => [p[0] * unitPx, p[1] * unitPx]);
+const pointsLeft = pointsL.map(p => [p[0] * unitPx, p[1] * unitPx]);
+
+export default function StandardLayer(props: {
+ width: number;
+ height: number;
+ leftPadding: number;
+ rightPadding: number;
+ topPadding: number;
+ bottomPadding: number;
+ columns: number;
+ rows: number;
+}) {
+ const xStartPx = props.leftPadding;
+ const xEndPx = props.width - props.rightPadding;
+ const xStepPx = (props.width - props.leftPadding - props.rightPadding) / props.columns;
+ const yStartPx = props.topPadding;
+ const yEndPx = props.height - props.bottomPadding;
+ const yStepPx = (props.height - props.topPadding - props.bottomPadding) / props.rows;
+
+ const canvasRef = useRef(null);
+
+ const draw = useCallback(
+ (ctx: CanvasRenderingContext2D) => {
+ ctx.strokeStyle = "#999";
+ // 偏移原点
+ const xOffset = (xEndPx - xStartPx) / 2;
+ const yOffset = yStepPx * 2;
+ const yMax = yEndPx - yStartPx - yOffset;
+ ctx.translate(xStartPx + xOffset, yStartPx + yOffset);
+ // 绘制原点交叉线
+ ctx.moveTo(-xOffset, 0);
+ ctx.lineTo(xOffset, 0);
+ ctx.moveTo(0, -yOffset);
+ ctx.lineTo(0, yMax);
+ ctx.stroke();
+
+ // 绘制x轴y轴 单位数值
+ ctx.beginPath();
+ ctx.fillStyle = "#333333";
+ ctx.textAlign = "center";
+ ctx.font = "normal 14px system";
+ for (let index = -4; index < 5; index++) {
+ ctx.fillText((index * 10).toString(), xStepPx * index, yMax + 20);
+ }
+ for (let index = -1; index < 5; index++) {
+ ctx.fillText((-index * 10).toString(), -xOffset - (index > 0 ? 18 : 14), yStepPx * index + 4);
+ }
+
+ // 绘制标准线
+ ctx.beginPath();
+ ctx.strokeStyle = "red";
+ // ---- 右侧
+ let point0 = pointsRight[0];
+ let point1 = pointsRight[1];
+ ctx.moveTo(point0[0], point0[1]);
+ let param = calculateCircleBelow(point0[0], point0[1], point1[0], point1[1], 300 * unitPx);
+ console.log(param);
+ ctx.arc(
+ param.center.x,
+ param.center.y,
+ 300 * unitPx,
+ param.anglesForCenter.startAngle,
+ param.anglesForCenter.endAngle
+ );
+
+ point0 = pointsRight[1];
+ point1 = pointsRight[2];
+ ctx.moveTo(point0[0], point0[1]);
+ param = calculateCircleBelow(point0[0], point0[1], point1[0], point1[1], 80 * unitPx);
+ console.log(param);
+ ctx.arc(
+ param.center.x,
+ param.center.y,
+ 80 * unitPx,
+ param.anglesForCenter.startAngle,
+ param.anglesForCenter.endAngle
+ );
+
+ point0 = pointsRight[2];
+ point1 = pointsRight[3];
+ ctx.moveTo(point0[0], point0[1]);
+ param = calculateCircleBelow(point0[0], point0[1], point1[0], point1[1], 13 * unitPx);
+ console.log(param);
+ ctx.arc(
+ param.center.x,
+ param.center.y,
+ 13 * unitPx,
+ param.anglesForCenter.startAngle,
+ param.anglesForCenter.endAngle
+ );
+
+ point0 = pointsRight[3];
+ point1 = pointsRight[4];
+ ctx.moveTo(point0[0], point0[1]);
+ ctx.lineTo(point1[0], point1[1]);
+
+ point0 = pointsRight[4];
+ point1 = pointsRight[5];
+ ctx.moveTo(point0[0], point0[1]);
+ param = calculateCircleAbove(point0[0], point0[1], point1[0], point1[1], 5 * unitPx);
+ console.log(param);
+ ctx.arc(param.center.x, param.center.y, 5 * unitPx, param.anglesForCenter.startAngle, param.anglesForCenter.endAngle);
+
+ point0 = pointsRight[5];
+ point1 = pointsRight[6];
+ ctx.moveTo(point0[0], point0[1]);
+ ctx.lineTo(point1[0], point1[1]);
+
+ // ---- 左侧
+ point0 = pointsLeft[0];
+ point1 = pointsLeft[1];
+ ctx.moveTo(point0[0], point0[1]);
+ ctx.lineTo(point1[0], point1[1]);
+
+ point0 = pointsLeft[1];
+ point1 = pointsLeft[2];
+ ctx.moveTo(point0[0], point0[1]);
+ param = calculateCircleAbove(point0[0], point0[1], point1[0], point1[1], 5 * unitPx);
+ console.log(param);
+ ctx.arc(param.center.x, param.center.y, 5 * unitPx, param.anglesForCenter.startAngle, param.anglesForCenter.endAngle);
+
+ point0 = pointsLeft[2];
+ point1 = pointsLeft[3];
+ ctx.moveTo(point0[0], point0[1]);
+ ctx.lineTo(point1[0], point1[1]);
+
+ point0 = pointsLeft[3];
+ point1 = pointsLeft[4];
+ ctx.moveTo(point0[0], point0[1]);
+ param = calculateCircleBelow(point0[0], point0[1], point1[0], point1[1], 13 * unitPx);
+ console.log(param);
+ ctx.arc(
+ param.center.x,
+ param.center.y,
+ 13 * unitPx,
+ param.anglesForCenter.startAngle,
+ param.anglesForCenter.endAngle
+ );
+
+ point0 = pointsLeft[4];
+ point1 = pointsLeft[5];
+ ctx.moveTo(point0[0], point0[1]);
+ param = calculateCircleBelow(point0[0], point0[1], point1[0], point1[1], 80 * unitPx);
+ console.log(param);
+ ctx.arc(
+ param.center.x,
+ param.center.y,
+ 80 * unitPx,
+ param.anglesForCenter.startAngle,
+ param.anglesForCenter.endAngle
+ );
+
+ point0 = pointsLeft[5];
+ point1 = pointsLeft[6];
+ ctx.moveTo(point0[0], point0[1]);
+ param = calculateCircleBelow(point0[0], point0[1], point1[0], point1[1], 300 * unitPx);
+ console.log(param);
+ ctx.arc(
+ param.center.x,
+ param.center.y,
+ 300 * unitPx,
+ param.anglesForCenter.startAngle,
+ param.anglesForCenter.endAngle
+ );
+
+ ctx.stroke();
+ // ctx.fillRect(100, 100, 150, 100); // 绘制一个矩形
+ },
+ [xEndPx, xStartPx, xStepPx, yEndPx, yStartPx, yStepPx]
+ );
+ useEffect(() => {
+ const canvas = canvasRef.current;
+ if (!canvas) return;
+
+ const context = canvas.getContext("2d");
+ if (!context) return;
+ draw(context);
+ }, [draw]);
+
+ return (
+
+
+
+ );
+}
diff --git a/src/utils/index.ts b/src/utils/index.ts
index 1c1eb9a..6c3a9b8 100644
--- a/src/utils/index.ts
+++ b/src/utils/index.ts
@@ -3,3 +3,63 @@ export function formatRemainTime(seconds: number) {
const sec = (seconds % 60).toFixed();
return min.padStart(2, "0") + ":" + sec.padStart(2, "0");
}
+
+export function calculateCircleInfo(x1: number, y1: number, x2: number, y2: number, radius: number) {
+ // 计算P1到P2的中点
+ const xm = (x1 + x2) / 2;
+ const ym = (y1 + y2) / 2;
+
+ // P1到P2的距离的一半
+ const dx = x2 - x1;
+ const dy = y2 - y1;
+ const d = Math.sqrt(dx * dx + dy * dy) / 2;
+
+ // 如果距离大于直径,则无法形成圆
+ if (d > radius) {
+ throw new Error("Given points are too far apart for the given radius.");
+ }
+
+ // 计算垂直平分线的方向向量
+ const len = Math.sqrt(radius * radius - d * d);
+ const nx = dy / (2 * d);
+ const ny = -dx / (2 * d);
+
+ // 计算两个可能的圆心
+ const cx1 = xm + nx * len;
+ const cy1 = ym + ny * len;
+ const cx2 = xm - nx * len;
+ const cy2 = ym - ny * len;
+
+ // 使用atan2计算起始角和结束角
+ function calculateAngle(cx: number, cy: number, px: number, py: number) {
+ return Math.atan2(py - cy, px - cx);
+ }
+
+ const startAngle1 = calculateAngle(cx1, cy1, x1, y1);
+ const endAngle1 = calculateAngle(cx1, cy1, x2, y2);
+ const startAngle2 = calculateAngle(cx2, cy2, x1, y1);
+ const endAngle2 = calculateAngle(cx2, cy2, x2, y2);
+
+ return [
+ { center: { x: cx1, y: cy1 }, anglesForCenter: { startAngle: startAngle1, endAngle: endAngle1 } },
+ { center: { x: cx2, y: cy2 }, anglesForCenter: { startAngle: startAngle2, endAngle: endAngle2 } },
+ ];
+}
+
+export type CenterAndAngle = ReturnType;
+export function calculateCircleBelow(x1: number, y1: number, x2: number, y2: number, radius: number) {
+ const results = calculateCircleInfo(x1, y1, x2, y2, radius);
+ if (results[0].center.y > results[1].center.y) {
+ return results[0];
+ } else {
+ return results[1];
+ }
+}
+export function calculateCircleAbove(x1: number, y1: number, x2: number, y2: number, radius: number) {
+ const results = calculateCircleInfo(x1, y1, x2, y2, radius);
+ if (results[0].center.y < results[1].center.y) {
+ return results[0];
+ } else {
+ return results[1];
+ }
+}