4 changed files with 370 additions and 1 deletions
-
2src/main/java/com/qyft/ms/app/service/WebSocketService.java
-
47src/main/java/com/qyft/ms/test/PathGenerator/MainFrame.java
-
208src/main/java/com/qyft/ms/test/PathGenerator/PathGenerator.java
-
114src/main/java/com/qyft/ms/test/PathGenerator/PathPanelJPanel.java
@ -0,0 +1,47 @@ |
|||||
|
package com.qyft.ms.test.PathGenerator; |
||||
|
|
||||
|
import javax.swing.*; |
||||
|
import java.awt.*; |
||||
|
import java.util.List; |
||||
|
|
||||
|
/** |
||||
|
* 主窗口,包含main方法。创建面板PathPanel,加载进JFrame展示。 |
||||
|
*/ |
||||
|
public class MainFrame extends JFrame { |
||||
|
|
||||
|
public static void main(String[] args) { |
||||
|
SwingUtilities.invokeLater(() -> { |
||||
|
MainFrame frame = new MainFrame(); |
||||
|
frame.setVisible(true); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
public MainFrame() { |
||||
|
super("Zigzag Path Demo (No Inner Classes)"); |
||||
|
|
||||
|
setDefaultCloseOperation(EXIT_ON_CLOSE); |
||||
|
|
||||
|
// 1. 定义边界 |
||||
|
int left = 0; |
||||
|
int right = 20; |
||||
|
int bottom = 0; |
||||
|
int top = 50; |
||||
|
int spacing = 2; |
||||
|
|
||||
|
// 2. 生成路径(从“左上角”往下的水平之字形) |
||||
|
List<Point> path = PathGenerator.generatePathPoints( |
||||
|
left, right, bottom, top, |
||||
|
spacing, |
||||
|
PathGenerator.MoveMode.HORIZONTAL_ZIGZAG_TOP_DOWN |
||||
|
); |
||||
|
|
||||
|
// 3. 创建面板并设置大小 |
||||
|
PathPanelJPanel panel = new PathPanelJPanel(left, right, bottom, top, path); |
||||
|
panel.setPreferredSize(new Dimension((right - left + 1) * 20, |
||||
|
(top - bottom + 1) * 20)); |
||||
|
getContentPane().add(panel); |
||||
|
|
||||
|
pack(); |
||||
|
setLocationRelativeTo(null); // 居中显示 |
||||
|
} |
||||
|
} |
@ -0,0 +1,208 @@ |
|||||
|
package com.qyft.ms.test.PathGenerator; |
||||
|
|
||||
|
import java.awt.*; |
||||
|
import java.util.ArrayList; |
||||
|
import java.util.List; |
||||
|
|
||||
|
/** |
||||
|
* 路径生成器:可选择水平之字形(从上往下) 或 垂直之字形(从左往右), |
||||
|
* 并考虑画笔spacing,向内收缩以避免越出外部边界。 |
||||
|
*/ |
||||
|
public class PathGenerator { |
||||
|
|
||||
|
/** 路径移动模式 */ |
||||
|
public enum MoveMode { |
||||
|
/** |
||||
|
* 水平之字形,从上往下。 |
||||
|
* 例如:先在(左上) -> (右上),向下spacing -> (右下) -> (左下) -> ... |
||||
|
*/ |
||||
|
HORIZONTAL_ZIGZAG_TOP_DOWN, |
||||
|
|
||||
|
/** |
||||
|
* 垂直之字形,从左往右。 |
||||
|
* 例如:先在(左上)->(左下),向右spacing->(右下)->(右上)-> ... |
||||
|
*/ |
||||
|
VERTICAL_ZIGZAG_LEFT_RIGHT |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 在 [left, right] x [bottom, top] 范围内生成“之字形”路径, |
||||
|
* 并考虑画笔 spacing(内缩以防越界)。 |
||||
|
* |
||||
|
* @param left 矩形左边界 (数学坐标: 左下角为原点) |
||||
|
* @param right 矩形右边界 |
||||
|
* @param bottom 矩形下边界 |
||||
|
* @param top 矩形上边界 |
||||
|
* @param spacing 画笔粗细/间距 (>0) |
||||
|
* @param mode HORIZONTAL_ZIGZAG_TOP_DOWN 或 VERTICAL_ZIGZAG_LEFT_RIGHT |
||||
|
* @return 之字形路径拐点列表 (每个拐点是 (x,y) 的int坐标) |
||||
|
*/ |
||||
|
public static List<Point> generatePathPoints( |
||||
|
int left, |
||||
|
int right, |
||||
|
int bottom, |
||||
|
int top, |
||||
|
int spacing, |
||||
|
MoveMode mode |
||||
|
) { |
||||
|
// 基本校验 |
||||
|
if (left >= right || bottom >= top) { |
||||
|
throw new IllegalArgumentException("无效矩形边界: left>=right 或 bottom>=top"); |
||||
|
} |
||||
|
if (spacing <= 0) { |
||||
|
throw new IllegalArgumentException("spacing必须>0"); |
||||
|
} |
||||
|
|
||||
|
// 1) 内缩,以防画笔越界 |
||||
|
int margin = spacing; // 留出 spacing 宽度,可改成 spacing/2 (半个笔刷) |
||||
|
int effLeft = left + margin; |
||||
|
int effRight = right - margin; |
||||
|
int effBottom = bottom + margin; |
||||
|
int effTop = top - margin; |
||||
|
|
||||
|
// 若收缩后无空间,则返回空集合 |
||||
|
if (effLeft > effRight || effBottom > effTop) { |
||||
|
System.out.println("内缩后无有效空间,返回空路径"); |
||||
|
return new ArrayList<>(); |
||||
|
} |
||||
|
|
||||
|
// 2) 根据mode选择不同的之字形算法 |
||||
|
switch (mode) { |
||||
|
case HORIZONTAL_ZIGZAG_TOP_DOWN: |
||||
|
return generateHorizontalZigzagTopDown( |
||||
|
effLeft, effRight, effBottom, effTop, spacing |
||||
|
); |
||||
|
case VERTICAL_ZIGZAG_LEFT_RIGHT: |
||||
|
return generateVerticalZigzagLeftRight( |
||||
|
effLeft, effRight, effBottom, effTop, spacing |
||||
|
); |
||||
|
default: |
||||
|
// 预留给更多模式扩展 |
||||
|
return new ArrayList<>(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* [effLeft..effRight] x [effBottom..effTop] 区域内, |
||||
|
* 从(左上)开始水平之字形向下扫描。 |
||||
|
* |
||||
|
* 流程: |
||||
|
* 1) 第1行: (effLeft, effTop) -> (effRight, effTop) |
||||
|
* 2) 下移 spacing |
||||
|
* 3) 第2行: (effRight, effTop - spacing) -> (effLeft, effTop - spacing) |
||||
|
* 4) 下移 spacing |
||||
|
* 重复,直到无法再下移。 |
||||
|
*/ |
||||
|
private static List<Point> generateHorizontalZigzagTopDown( |
||||
|
int effLeft, |
||||
|
int effRight, |
||||
|
int effBottom, |
||||
|
int effTop, |
||||
|
int spacing |
||||
|
) { |
||||
|
List<Point> result = new ArrayList<>(); |
||||
|
int currentX = effLeft; |
||||
|
int currentY = effTop; |
||||
|
boolean goingRight = true; |
||||
|
|
||||
|
// 起点 |
||||
|
result.add(new Point(currentX, currentY)); |
||||
|
|
||||
|
while (true) { |
||||
|
// 水平移动到对侧 |
||||
|
if (goingRight) { |
||||
|
currentX = effRight; |
||||
|
} else { |
||||
|
currentX = effLeft; |
||||
|
} |
||||
|
result.add(new Point(currentX, currentY)); |
||||
|
|
||||
|
// 往下移动 spacing |
||||
|
int nextY = currentY - spacing; |
||||
|
if (nextY < effBottom) { |
||||
|
break; // 无法再往下 |
||||
|
} |
||||
|
currentY = nextY; |
||||
|
result.add(new Point(currentX, currentY)); |
||||
|
|
||||
|
// 反转方向 |
||||
|
goingRight = !goingRight; |
||||
|
} |
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* [effLeft..effRight] x [effBottom..effTop] 区域内, |
||||
|
* 从(左上)开始垂直之字形向右扫描。 |
||||
|
* |
||||
|
* 流程: |
||||
|
* 1) 第1列: (effLeft, effTop) -> (effLeft, effBottom) |
||||
|
* 2) 右移 spacing |
||||
|
* 3) 第2列: (effLeft+spacing, effBottom) -> (effLeft+spacing, effTop) |
||||
|
* 4) 右移 spacing |
||||
|
* 重复,直到无法再右移。 |
||||
|
*/ |
||||
|
private static List<Point> generateVerticalZigzagLeftRight( |
||||
|
int effLeft, |
||||
|
int effRight, |
||||
|
int effBottom, |
||||
|
int effTop, |
||||
|
int spacing |
||||
|
) { |
||||
|
List<Point> result = new ArrayList<>(); |
||||
|
int currentX = effLeft; |
||||
|
int currentY = effTop; |
||||
|
boolean goingDown = true; // 第一列先从上往下 |
||||
|
|
||||
|
// 起点 |
||||
|
result.add(new Point(currentX, currentY)); |
||||
|
|
||||
|
while (true) { |
||||
|
// 垂直移动到对侧 |
||||
|
if (goingDown) { |
||||
|
currentY = effBottom; |
||||
|
} else { |
||||
|
currentY = effTop; |
||||
|
} |
||||
|
result.add(new Point(currentX, currentY)); |
||||
|
|
||||
|
// 向右移动 spacing |
||||
|
int nextX = currentX + spacing; |
||||
|
if (nextX > effRight) { |
||||
|
break; // 无法再右移 |
||||
|
} |
||||
|
currentX = nextX; |
||||
|
result.add(new Point(currentX, currentY)); |
||||
|
|
||||
|
// 反转 |
||||
|
goingDown = !goingDown; |
||||
|
} |
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
// ============= 简单测试 ============= |
||||
|
public static void main(String[] args) { |
||||
|
int left = 0, right = 10; |
||||
|
int bottom = 0, top = 6; |
||||
|
int spacing = 2; |
||||
|
|
||||
|
// 1) 测试水平之字形(从上往下) |
||||
|
System.out.println("=== HORIZONTAL_ZIGZAG_TOP_DOWN ==="); |
||||
|
List<Point> horizontalPath = generatePathPoints( |
||||
|
left, right, bottom, top, spacing, MoveMode.HORIZONTAL_ZIGZAG_TOP_DOWN |
||||
|
); |
||||
|
for (Point p : horizontalPath) { |
||||
|
System.out.println(p); |
||||
|
} |
||||
|
|
||||
|
// 2) 测试垂直之字形(从左往右) |
||||
|
System.out.println("\n=== VERTICAL_ZIGZAG_LEFT_RIGHT ==="); |
||||
|
List<Point> verticalPath = generatePathPoints( |
||||
|
left, right, bottom, top, spacing, MoveMode.VERTICAL_ZIGZAG_LEFT_RIGHT |
||||
|
); |
||||
|
for (Point p : verticalPath) { |
||||
|
System.out.println(p); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
@ -0,0 +1,114 @@ |
|||||
|
package com.qyft.ms.test.PathGenerator; |
||||
|
|
||||
|
import javax.swing.*; |
||||
|
import java.awt.*; |
||||
|
import java.util.List; |
||||
|
|
||||
|
/** |
||||
|
* 自定义面板:绘制网格和路径。 |
||||
|
* 坐标系:数学上left..right, bottom..top;GUI需翻转y轴。 |
||||
|
*/ |
||||
|
public class PathPanelJPanel extends JPanel { |
||||
|
|
||||
|
private final int left; |
||||
|
private final int right; |
||||
|
private final int bottom; |
||||
|
private final int top; |
||||
|
private final List<Point> pathPoints; |
||||
|
|
||||
|
// 每单位1点对应多少像素,可自行调节 |
||||
|
private static final int CELL_SIZE = 20; |
||||
|
|
||||
|
public PathPanelJPanel(int left, int right, int bottom, int top, List<Point> pathPoints) { |
||||
|
this.left = left; |
||||
|
this.right = right; |
||||
|
this.bottom = bottom; |
||||
|
this.top = top; |
||||
|
this.pathPoints = pathPoints; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected void paintComponent(Graphics g) { |
||||
|
super.paintComponent(g); |
||||
|
|
||||
|
Graphics2D g2d = (Graphics2D) g.create(); |
||||
|
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); |
||||
|
|
||||
|
// 1) 画网格 |
||||
|
drawGrid(g2d); |
||||
|
// 2) 画路径 |
||||
|
drawPath(g2d); |
||||
|
|
||||
|
g2d.dispose(); |
||||
|
} |
||||
|
|
||||
|
/** 绘制网格线和坐标标签 */ |
||||
|
private void drawGrid(Graphics2D g2d) { |
||||
|
g2d.setColor(Color.LIGHT_GRAY); |
||||
|
|
||||
|
// 画垂直线 |
||||
|
for (int x = left; x <= right; x++) { |
||||
|
int px = worldToPanelX(x); |
||||
|
g2d.drawLine(px, worldToPanelY(bottom), px, worldToPanelY(top)); |
||||
|
} |
||||
|
// 画水平线 |
||||
|
for (int y = bottom; y <= top; y++) { |
||||
|
int py = worldToPanelY(y); |
||||
|
g2d.drawLine(worldToPanelX(left), py, worldToPanelX(right), py); |
||||
|
} |
||||
|
|
||||
|
// 坐标文本标注(可选) |
||||
|
// g2d.setColor(Color.BLACK); |
||||
|
// for (int x = left; x <= right; x++) { |
||||
|
// for (int y = bottom; y <= top; y++) { |
||||
|
// int px = worldToPanelX(x) + 2; |
||||
|
// int py = worldToPanelY(y) - 2; |
||||
|
// g2d.drawString(x + "," + y, px, py); |
||||
|
// } |
||||
|
// } |
||||
|
} |
||||
|
|
||||
|
/** 绘制之字形路径及每个拐点小圆,边绘制边输出坐标 */ |
||||
|
private void drawPath(Graphics2D g2d) { |
||||
|
// 先用红色线段连接拐点 |
||||
|
g2d.setColor(Color.RED); |
||||
|
g2d.setStroke(new BasicStroke(2f)); |
||||
|
|
||||
|
for (int i = 0; i < pathPoints.size() - 1; i++) { |
||||
|
Point p1 = pathPoints.get(i); |
||||
|
Point p2 = pathPoints.get(i + 1); |
||||
|
|
||||
|
int x1 = worldToPanelX(p1.x); |
||||
|
int y1 = worldToPanelY(p1.y); |
||||
|
int x2 = worldToPanelX(p2.x); |
||||
|
int y2 = worldToPanelY(p2.y); |
||||
|
|
||||
|
g2d.drawLine(x1, y1, x2, y2); |
||||
|
} |
||||
|
|
||||
|
// 再用蓝色在拐点上画小圆点 |
||||
|
g2d.setColor(Color.BLUE); |
||||
|
for (Point p : pathPoints) { |
||||
|
int cx = worldToPanelX(p.x); |
||||
|
int cy = worldToPanelY(p.y); |
||||
|
|
||||
|
// 同时打印到控制台,说明“已经画到了这个点” |
||||
|
System.out.println("画点: (" + p.x + ", " + p.y + ")"); |
||||
|
|
||||
|
g2d.fillOval(cx - 4, cy - 4, 8, 8); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** 坐标转换:世界X -> 面板像素X */ |
||||
|
private int worldToPanelX(int worldX) { |
||||
|
return (worldX - left) * CELL_SIZE; |
||||
|
} |
||||
|
|
||||
|
/** 坐标转换:世界Y -> 面板像素Y(翻转y) */ |
||||
|
private int worldToPanelY(int worldY) { |
||||
|
// 矩形整体高度(像素) |
||||
|
int totalHeight = (top - bottom) * CELL_SIZE; |
||||
|
// 自下而上 -> GUI坐标需自上而下翻转 |
||||
|
return totalHeight - (worldY - bottom) * CELL_SIZE; |
||||
|
} |
||||
|
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue