package zhangaiwu;
|
import javax.swing.*;
|
import java.awt.*;
|
import java.awt.geom.AffineTransform;
|
import java.awt.geom.Ellipse2D;
|
import java.awt.geom.Path2D;
|
import java.awt.geom.Point2D;
|
import java.util.List;
|
import java.util.ArrayList;
|
|
/**
|
* 障碍物绘制类 - 负责绘制地块中的障碍物
|
*/
|
public class Obstacledraw {
|
|
// 颜色定义
|
private static final Color CIRCLE_FILL_COLOR = new Color(255, 182, 193, 120); // 圆形填充色 - 浅粉红
|
private static final Color CIRCLE_BORDER_COLOR = new Color(199, 21, 133); // 圆形边框色 - 深粉红
|
private static final Color POLYGON_FILL_COLOR = new Color(173, 216, 230, 120); // 多边形填充色 - 浅蓝
|
private static final Color POLYGON_BORDER_COLOR = new Color(25, 25, 112); // 多边形边框色 - 深蓝
|
private static final Color OBSTACLE_LABEL_COLOR = Color.BLACK;
|
private static final Color OBSTACLE_POINT_COLOR = Color.RED;
|
|
// 尺寸定义
|
private static final double OBSTACLE_POINT_SIZE = 0.1; // 障碍物控制点大小(米)
|
private static final float DEFAULT_BORDER_WIDTH = 1.0f;
|
private static final float SELECTED_BORDER_WIDTH = 2.5f;
|
|
/**
|
* 绘制地块的所有障碍物
|
*
|
* @param g2d 图形上下文
|
* @param obstacles 障碍物列表
|
* @param scale 缩放比例
|
* @param selectedObstacleName 选中的障碍物名称(可为null)
|
*/
|
public static void drawObstacles(Graphics2D g2d, List<Obstacledge.Obstacle> obstacles,
|
double scale, String selectedObstacleName) {
|
if (obstacles == null || obstacles.isEmpty()) {
|
return;
|
}
|
|
// 保存原始变换
|
AffineTransform originalTransform = g2d.getTransform();
|
|
// 应用反锯齿
|
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
|
// 绘制每个障碍物
|
for (Obstacledge.Obstacle obstacle : obstacles) {
|
if (obstacle == null || !obstacle.isValid()) {
|
continue;
|
}
|
|
// 检查是否被选中
|
boolean isSelected = selectedObstacleName != null &&
|
selectedObstacleName.equals(obstacle.getObstacleName());
|
|
// 根据形状绘制
|
switch (obstacle.getShape()) {
|
case CIRCLE:
|
drawCircleObstacle(g2d, obstacle, scale, isSelected);
|
break;
|
case POLYGON:
|
drawPolygonObstacle(g2d, obstacle, scale, isSelected);
|
break;
|
}
|
|
// 绘制障碍物标签
|
drawObstacleLabel(g2d, obstacle, scale);
|
}
|
|
// 恢复原始变换
|
g2d.setTransform(originalTransform);
|
}
|
|
/**
|
* 绘制圆形障碍物
|
*/
|
private static void drawCircleObstacle(Graphics2D g2d, Obstacledge.Obstacle obstacle,
|
double scale, boolean isSelected) {
|
List<Obstacledge.XYCoordinate> xyCoords = obstacle.getXyCoordinates();
|
if (xyCoords.size() < 2) {
|
return;
|
}
|
|
// 获取圆心和圆上一点
|
Obstacledge.XYCoordinate center = xyCoords.get(0);
|
Obstacledge.XYCoordinate pointOnCircle = xyCoords.get(1);
|
|
// 计算半径
|
double dx = pointOnCircle.getX() - center.getX();
|
double dy = pointOnCircle.getY() - center.getY();
|
double radius = Math.sqrt(dx * dx + dy * dy);
|
|
if (radius <= 0) {
|
return;
|
}
|
|
// 计算绘制位置
|
double x = center.getX() - radius;
|
double y = center.getY() - radius;
|
double diameter = radius * 2;
|
|
// 绘制圆形
|
Ellipse2D.Double circle = new Ellipse2D.Double(x, y, diameter, diameter);
|
|
// 设置填充颜色
|
g2d.setColor(CIRCLE_FILL_COLOR);
|
g2d.fill(circle);
|
|
// 设置边框颜色和宽度
|
if (isSelected) {
|
g2d.setColor(CIRCLE_BORDER_COLOR.darker());
|
g2d.setStroke(new BasicStroke(SELECTED_BORDER_WIDTH / (float)scale));
|
} else {
|
g2d.setColor(CIRCLE_BORDER_COLOR);
|
g2d.setStroke(new BasicStroke(DEFAULT_BORDER_WIDTH / (float)scale));
|
}
|
g2d.draw(circle);
|
|
// 绘制圆心和控制点
|
drawControlPoints(g2d, obstacle, scale, isSelected);
|
}
|
|
/**
|
* 绘制多边形障碍物
|
*/
|
private static void drawPolygonObstacle(Graphics2D g2d, Obstacledge.Obstacle obstacle,
|
double scale, boolean isSelected) {
|
List<Obstacledge.XYCoordinate> xyCoords = obstacle.getXyCoordinates();
|
if (xyCoords.size() < 3) {
|
return;
|
}
|
|
// 创建多边形路径
|
Path2D.Double polygon = new Path2D.Double();
|
|
// 移动到第一个点
|
Obstacledge.XYCoordinate firstCoord = xyCoords.get(0);
|
polygon.moveTo(firstCoord.getX(), firstCoord.getY());
|
|
// 添加其他点
|
for (int i = 1; i < xyCoords.size(); i++) {
|
Obstacledge.XYCoordinate coord = xyCoords.get(i);
|
polygon.lineTo(coord.getX(), coord.getY());
|
}
|
|
// 闭合多边形
|
polygon.closePath();
|
|
// 设置填充颜色
|
g2d.setColor(POLYGON_FILL_COLOR);
|
g2d.fill(polygon);
|
|
// 设置边框颜色和宽度
|
if (isSelected) {
|
g2d.setColor(POLYGON_BORDER_COLOR.darker());
|
g2d.setStroke(new BasicStroke(SELECTED_BORDER_WIDTH / (float)scale));
|
} else {
|
g2d.setColor(POLYGON_BORDER_COLOR);
|
g2d.setStroke(new BasicStroke(DEFAULT_BORDER_WIDTH / (float)scale));
|
}
|
g2d.draw(polygon);
|
|
// 绘制控制点
|
drawControlPoints(g2d, obstacle, scale, isSelected);
|
}
|
|
/**
|
* 绘制障碍物控制点
|
*/
|
private static void drawControlPoints(Graphics2D g2d, Obstacledge.Obstacle obstacle,
|
double scale, boolean isSelected) {
|
List<Obstacledge.XYCoordinate> xyCoords = obstacle.getXyCoordinates();
|
if (xyCoords.isEmpty()) {
|
return;
|
}
|
|
// 设置控制点颜色和大小
|
g2d.setColor(OBSTACLE_POINT_COLOR);
|
double pointSize = OBSTACLE_POINT_SIZE;
|
|
// 如果是选中的障碍物,绘制所有控制点
|
if (isSelected) {
|
for (Obstacledge.XYCoordinate coord : xyCoords) {
|
double x = coord.getX() - pointSize / 2;
|
double y = coord.getY() - pointSize / 2;
|
Ellipse2D.Double point = new Ellipse2D.Double(x, y, pointSize, pointSize);
|
g2d.fill(point);
|
}
|
}
|
// 否则,只绘制第一个控制点(圆形障碍物为圆心,多边形障碍物为第一个顶点)
|
else {
|
Obstacledge.XYCoordinate firstCoord = xyCoords.get(0);
|
double x = firstCoord.getX() - pointSize / 2;
|
double y = firstCoord.getY() - pointSize / 2;
|
Ellipse2D.Double point = new Ellipse2D.Double(x, y, pointSize, pointSize);
|
g2d.fill(point);
|
}
|
}
|
|
/**
|
* 绘制障碍物标签
|
*/
|
private static void drawObstacleLabel(Graphics2D g2d, Obstacledge.Obstacle obstacle,
|
double scale) {
|
List<Obstacledge.XYCoordinate> xyCoords = obstacle.getXyCoordinates();
|
if (xyCoords.isEmpty()) {
|
return;
|
}
|
|
double centerX;
|
double centerY;
|
|
Obstacledge.ObstacleShape shape = obstacle.getShape();
|
if (shape == Obstacledge.ObstacleShape.CIRCLE) {
|
Obstacledge.XYCoordinate centerCoord = xyCoords.get(0);
|
centerX = centerCoord.getX();
|
centerY = centerCoord.getY();
|
} else {
|
Point2D.Double centroid = computePolygonCentroid(xyCoords);
|
centerX = centroid.x;
|
centerY = centroid.y;
|
}
|
|
// 获取障碍物名称
|
String obstacleName = obstacle.getObstacleName();
|
if (obstacleName == null || obstacleName.trim().isEmpty()) {
|
obstacleName = "障碍物";
|
} else {
|
obstacleName = obstacleName.trim();
|
}
|
|
// 设置字体和颜色
|
g2d.setColor(OBSTACLE_LABEL_COLOR);
|
|
// 根据缩放比例调整字体大小
|
int fontSize = (int)(10 / scale);
|
fontSize = Math.max(8, Math.min(fontSize, 14)); // 限制字体大小范围
|
|
g2d.setFont(new Font("微软雅黑", Font.PLAIN, fontSize));
|
|
// 绘制标签
|
String label = obstacleName;
|
FontMetrics metrics = g2d.getFontMetrics();
|
int textWidth = metrics.stringWidth(label);
|
int textHeight = metrics.getHeight();
|
|
// 在中心点绘制标签
|
int textX = (int)(centerX - textWidth / 2.0);
|
int textY = (int)(centerY + textHeight / 4.0); // 稍微向下偏移
|
|
g2d.drawString(label, textX, textY);
|
}
|
|
private static Point2D.Double computePolygonCentroid(List<Obstacledge.XYCoordinate> xyCoords) {
|
double area = 0.0;
|
double cx = 0.0;
|
double cy = 0.0;
|
int n = xyCoords.size();
|
for (int i = 0; i < n; i++) {
|
Obstacledge.XYCoordinate current = xyCoords.get(i);
|
Obstacledge.XYCoordinate next = xyCoords.get((i + 1) % n);
|
double x0 = current.getX();
|
double y0 = current.getY();
|
double x1 = next.getX();
|
double y1 = next.getY();
|
double cross = x0 * y1 - x1 * y0;
|
area += cross;
|
cx += (x0 + x1) * cross;
|
cy += (y0 + y1) * cross;
|
}
|
|
double areaFactor = area * 0.5;
|
if (Math.abs(areaFactor) < 1e-9) {
|
double avgX = 0.0;
|
double avgY = 0.0;
|
for (Obstacledge.XYCoordinate coord : xyCoords) {
|
avgX += coord.getX();
|
avgY += coord.getY();
|
}
|
int size = Math.max(1, xyCoords.size());
|
return new Point2D.Double(avgX / size, avgY / size);
|
}
|
|
double centroidX = cx / (6.0 * areaFactor);
|
double centroidY = cy / (6.0 * areaFactor);
|
return new Point2D.Double(centroidX, centroidY);
|
}
|
|
/**
|
* 检查点是否在障碍物内
|
*
|
* @param worldPoint 世界坐标点
|
* @param obstacle 障碍物
|
* @return 如果点在障碍物内返回true,否则返回false
|
*/
|
public static boolean isPointInObstacle(Point2D.Double worldPoint, Obstacledge.Obstacle obstacle) {
|
if (worldPoint == null || obstacle == null || !obstacle.isValid()) {
|
return false;
|
}
|
|
List<Obstacledge.XYCoordinate> xyCoords = obstacle.getXyCoordinates();
|
if (xyCoords.isEmpty()) {
|
return false;
|
}
|
|
// 根据障碍物形状进行判断
|
switch (obstacle.getShape()) {
|
case CIRCLE:
|
return isPointInCircleObstacle(worldPoint, xyCoords);
|
case POLYGON:
|
return isPointInPolygonObstacle(worldPoint, xyCoords);
|
default:
|
return false;
|
}
|
}
|
|
/**
|
* 检查点是否在圆形障碍物内
|
*/
|
private static boolean isPointInCircleObstacle(Point2D.Double worldPoint,
|
List<Obstacledge.XYCoordinate> xyCoords) {
|
if (xyCoords.size() < 2) {
|
return false;
|
}
|
|
// 圆心
|
Obstacledge.XYCoordinate center = xyCoords.get(0);
|
|
// 圆上一点
|
Obstacledge.XYCoordinate pointOnCircle = xyCoords.get(1);
|
|
// 计算半径
|
double dx = pointOnCircle.getX() - center.getX();
|
double dy = pointOnCircle.getY() - center.getY();
|
double radius = Math.sqrt(dx * dx + dy * dy);
|
|
// 计算点到圆心的距离
|
double pointDx = worldPoint.x - center.getX();
|
double pointDy = worldPoint.y - center.getY();
|
double distance = Math.sqrt(pointDx * pointDx + pointDy * pointDy);
|
|
return distance <= radius;
|
}
|
|
/**
|
* 检查点是否在多边形障碍物内
|
*/
|
private static boolean isPointInPolygonObstacle(Point2D.Double worldPoint,
|
List<Obstacledge.XYCoordinate> xyCoords) {
|
if (xyCoords.size() < 3) {
|
return false;
|
}
|
|
// 使用射线法判断点是否在多边形内
|
boolean inside = false;
|
int n = xyCoords.size();
|
|
for (int i = 0, j = n - 1; i < n; j = i++) {
|
double xi = xyCoords.get(i).getX();
|
double yi = xyCoords.get(i).getY();
|
double xj = xyCoords.get(j).getX();
|
double yj = xyCoords.get(j).getY();
|
|
boolean intersect = ((yi > worldPoint.y) != (yj > worldPoint.y)) &&
|
(worldPoint.x < (xj - xi) * (worldPoint.y - yi) / (yj - yi) + xi);
|
|
if (intersect) {
|
inside = !inside;
|
}
|
}
|
|
return inside;
|
}
|
|
/**
|
* 检查点是否在障碍物的控制点上
|
*
|
* @param worldPoint 世界坐标点
|
* @param obstacle 障碍物
|
* @param scale 缩放比例
|
* @return 控制点索引(从0开始),如果不在控制点上返回-1
|
*/
|
public static int getControlPointIndex(Point2D.Double worldPoint,
|
Obstacledge.Obstacle obstacle,
|
double scale) {
|
if (worldPoint == null || obstacle == null || !obstacle.isValid()) {
|
return -1;
|
}
|
|
List<Obstacledge.XYCoordinate> xyCoords = obstacle.getXyCoordinates();
|
if (xyCoords.isEmpty()) {
|
return -1;
|
}
|
|
// 根据障碍物形状计算选择阈值
|
double selectionThreshold = OBSTACLE_POINT_SIZE * 2 / scale;
|
|
// 检查每个控制点
|
for (int i = 0; i < xyCoords.size(); i++) {
|
Obstacledge.XYCoordinate coord = xyCoords.get(i);
|
|
double dx = worldPoint.x - coord.getX();
|
double dy = worldPoint.y - coord.getY();
|
double distance = Math.sqrt(dx * dx + dy * dy);
|
|
if (distance <= selectionThreshold) {
|
return i;
|
}
|
}
|
|
return -1;
|
}
|
|
/**
|
* 获取障碍物的边界框
|
*
|
* @param obstacle 障碍物
|
* @return 边界框(minX, minY, maxX, maxY),如果无效返回null
|
*/
|
public static double[] getObstacleBounds(Obstacledge.Obstacle obstacle) {
|
if (obstacle == null || !obstacle.isValid()) {
|
return null;
|
}
|
|
List<Obstacledge.XYCoordinate> xyCoords = obstacle.getXyCoordinates();
|
if (xyCoords.isEmpty()) {
|
return null;
|
}
|
|
double minX = Double.MAX_VALUE;
|
double minY = Double.MAX_VALUE;
|
double maxX = -Double.MAX_VALUE;
|
double maxY = -Double.MAX_VALUE;
|
|
// 根据障碍物形状计算边界
|
switch (obstacle.getShape()) {
|
case CIRCLE:
|
return getCircleObstacleBounds(xyCoords);
|
case POLYGON:
|
return getPolygonObstacleBounds(xyCoords);
|
default:
|
return null;
|
}
|
}
|
|
/**
|
* 获取圆形障碍物的边界框
|
*/
|
private static double[] getCircleObstacleBounds(List<Obstacledge.XYCoordinate> xyCoords) {
|
if (xyCoords.size() < 2) {
|
return null;
|
}
|
|
// 圆心
|
Obstacledge.XYCoordinate center = xyCoords.get(0);
|
|
// 圆上一点
|
Obstacledge.XYCoordinate pointOnCircle = xyCoords.get(1);
|
|
// 计算半径
|
double dx = pointOnCircle.getX() - center.getX();
|
double dy = pointOnCircle.getY() - center.getY();
|
double radius = Math.sqrt(dx * dx + dy * dy);
|
|
// 计算边界
|
double minX = center.getX() - radius;
|
double minY = center.getY() - radius;
|
double maxX = center.getX() + radius;
|
double maxY = center.getY() + radius;
|
|
return new double[]{minX, minY, maxX, maxY};
|
}
|
|
/**
|
* 获取多边形障碍物的边界框
|
*/
|
private static double[] getPolygonObstacleBounds(List<Obstacledge.XYCoordinate> xyCoords) {
|
if (xyCoords.size() < 3) {
|
return null;
|
}
|
|
double minX = Double.MAX_VALUE;
|
double minY = Double.MAX_VALUE;
|
double maxX = -Double.MAX_VALUE;
|
double maxY = -Double.MAX_VALUE;
|
|
for (Obstacledge.XYCoordinate coord : xyCoords) {
|
minX = Math.min(minX, coord.getX());
|
minY = Math.min(minY, coord.getY());
|
maxX = Math.max(maxX, coord.getX());
|
maxY = Math.max(maxY, coord.getY());
|
}
|
|
return new double[]{minX, minY, maxX, maxY};
|
}
|
|
/**
|
* 将障碍物列表转换为绘图用的点列表
|
*
|
* @param obstacles 障碍物列表
|
* @return 所有障碍物控制点的列表
|
*/
|
public static List<Point2D.Double> getObstaclePoints(List<Obstacledge.Obstacle> obstacles) {
|
List<Point2D.Double> points = new ArrayList<>();
|
|
if (obstacles == null || obstacles.isEmpty()) {
|
return points;
|
}
|
|
for (Obstacledge.Obstacle obstacle : obstacles) {
|
if (obstacle == null || !obstacle.isValid()) {
|
continue;
|
}
|
|
List<Obstacledge.XYCoordinate> xyCoords = obstacle.getXyCoordinates();
|
for (Obstacledge.XYCoordinate coord : xyCoords) {
|
points.add(new Point2D.Double(coord.getX(), coord.getY()));
|
}
|
}
|
|
return points;
|
}
|
|
/**
|
* 计算所有障碍物的总边界框
|
*
|
* @param obstacles 障碍物列表
|
* @return 边界框(minX, minY, maxX, maxY),如果没有障碍物返回null
|
*/
|
public static double[] getAllObstaclesBounds(List<Obstacledge.Obstacle> obstacles) {
|
if (obstacles == null || obstacles.isEmpty()) {
|
return null;
|
}
|
|
double minX = Double.MAX_VALUE;
|
double minY = Double.MAX_VALUE;
|
double maxX = -Double.MAX_VALUE;
|
double maxY = -Double.MAX_VALUE;
|
boolean hasBounds = false;
|
|
for (Obstacledge.Obstacle obstacle : obstacles) {
|
double[] bounds = getObstacleBounds(obstacle);
|
if (bounds != null) {
|
minX = Math.min(minX, bounds[0]);
|
minY = Math.min(minY, bounds[1]);
|
maxX = Math.max(maxX, bounds[2]);
|
maxY = Math.max(maxY, bounds[3]);
|
hasBounds = true;
|
}
|
}
|
|
if (!hasBounds) {
|
return null;
|
}
|
|
return new double[]{minX, minY, maxX, maxY};
|
}
|
|
/**
|
* 绘制障碍物编辑模式下的辅助线
|
*
|
* @param g2d 图形上下文
|
* @param obstacle 障碍物
|
* @param scale 缩放比例
|
* @param isDragging 是否正在拖拽
|
* @param dragPointIndex 拖拽的控制点索引
|
*/
|
public static void drawEditingGuide(Graphics2D g2d, Obstacledge.Obstacle obstacle,
|
double scale, boolean isDragging, int dragPointIndex) {
|
if (obstacle == null || !obstacle.isValid()) {
|
return;
|
}
|
|
List<Obstacledge.XYCoordinate> xyCoords = obstacle.getXyCoordinates();
|
if (xyCoords.isEmpty()) {
|
return;
|
}
|
|
// 设置辅助线样式
|
g2d.setColor(new Color(0, 100, 0, 150)); // 半透明的深绿色
|
g2d.setStroke(new BasicStroke(1.0f / (float)scale,
|
BasicStroke.CAP_ROUND,
|
BasicStroke.JOIN_ROUND,
|
1.0f,
|
new float[]{5.0f / (float)scale, 5.0f / (float)scale},
|
0.0f));
|
|
// 根据障碍物形状绘制辅助线
|
switch (obstacle.getShape()) {
|
case CIRCLE:
|
drawCircleEditingGuide(g2d, xyCoords, isDragging, dragPointIndex);
|
break;
|
case POLYGON:
|
drawPolygonEditingGuide(g2d, xyCoords, isDragging, dragPointIndex);
|
break;
|
}
|
}
|
|
/**
|
* 绘制圆形障碍物编辑模式下的辅助线
|
*/
|
private static void drawCircleEditingGuide(Graphics2D g2d,
|
List<Obstacledge.XYCoordinate> xyCoords,
|
boolean isDragging, int dragPointIndex) {
|
if (xyCoords.size() < 2) {
|
return;
|
}
|
|
Obstacledge.XYCoordinate center = xyCoords.get(0);
|
Obstacledge.XYCoordinate pointOnCircle = xyCoords.get(1);
|
|
// 绘制半径线
|
g2d.drawLine((int)center.getX(), (int)center.getY(),
|
(int)pointOnCircle.getX(), (int)pointOnCircle.getY());
|
|
// 绘制中心十字线
|
int crossSize = 5;
|
g2d.drawLine((int)center.getX() - crossSize, (int)center.getY(),
|
(int)center.getX() + crossSize, (int)center.getY());
|
g2d.drawLine((int)center.getX(), (int)center.getY() - crossSize,
|
(int)center.getX(), (int)center.getY() + crossSize);
|
|
// 如果正在拖拽控制点,绘制拖拽线
|
if (isDragging && dragPointIndex >= 0 && dragPointIndex < xyCoords.size()) {
|
g2d.setColor(Color.RED);
|
g2d.setStroke(new BasicStroke(2.0f));
|
|
Obstacledge.XYCoordinate draggedPoint = xyCoords.get(dragPointIndex);
|
|
// 绘制从控制点到鼠标的连线
|
// 这里需要外部传入鼠标位置,暂时不实现
|
}
|
}
|
|
/**
|
* 绘制多边形障碍物编辑模式下的辅助线
|
*/
|
private static void drawPolygonEditingGuide(Graphics2D g2d,
|
List<Obstacledge.XYCoordinate> xyCoords,
|
boolean isDragging, int dragPointIndex) {
|
if (xyCoords.size() < 3) {
|
return;
|
}
|
|
// 绘制顶点之间的辅助线
|
int n = xyCoords.size();
|
for (int i = 0; i < n; i++) {
|
Obstacledge.XYCoordinate current = xyCoords.get(i);
|
Obstacledge.XYCoordinate next = xyCoords.get((i + 1) % n);
|
|
g2d.drawLine((int)current.getX(), (int)current.getY(),
|
(int)next.getX(), (int)next.getY());
|
|
// 绘制每个顶点的十字线
|
int crossSize = 3;
|
g2d.drawLine((int)current.getX() - crossSize, (int)current.getY(),
|
(int)current.getX() + crossSize, (int)current.getY());
|
g2d.drawLine((int)current.getX(), (int)current.getY() - crossSize,
|
(int)current.getX(), (int)current.getY() + crossSize);
|
}
|
|
// 如果正在拖拽控制点,绘制拖拽线
|
if (isDragging && dragPointIndex >= 0 && dragPointIndex < xyCoords.size()) {
|
g2d.setColor(Color.RED);
|
g2d.setStroke(new BasicStroke(2.0f));
|
|
Obstacledge.XYCoordinate draggedPoint = xyCoords.get(dragPointIndex);
|
|
// 绘制从控制点到鼠标的连线
|
// 这里需要外部传入鼠标位置,暂时不实现
|
}
|
}
|
}
|