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