前言
这个小工具来源一个游戏,, 是什么游戏我这里先不说 [placeholder] 呵呵 那天想起了再回来填充游戏名吧, 这个东西主要是作为这个游戏的辅助工具, 为了这个游戏我在网上搜索了相关的软件, 其中之一便是pointFix这个软件, 似乎是德国人做的,, 我也给作者发过邮件, 提过一些建议, 作者非常友好
请先了解一下这个软件的功能, 在向下看
但是 我在玩这个游戏的时候啊, 出现了一个问题, 就是我需要截取当前屏幕, 并进行涂鸦, 我想绘制一个直线箭头, 但是 我只能指定作者预定义的一些大小,,, 但是 我需要精确啊,, 不然玩不过别人,, 然后就萌生了做一个类似于这个软件功能的一个小工具的想法
然后 因为之前的一次扯淡的文档操作, 导致了我的项目中所有的中文注释乱码,, so 这个程序的注释也没了, 而且 缩进也不再是我之前写的的时候的风格了
但是 查了一下日记, 这个程序大概是3月21下午做的吧。、。
不过有些朋友是变态啊,, 即使 我是用了工具, 也打不赢。。。
问题描述
做一个工具, 实现截取当前屏幕, 并让用户涂鸦[我这里 只写了三种图形, 一个直线, 一个带箭头的直线, 一个带箭头的带圈圈的直线, 有兴趣的朋友可以继续扩展]
还有一个问题, 我这里并没有提供ui来操作来配置各个宽度啊, 颜色啊, 需要绘制的形状啊啥的, 只能通过配置文件进行配置, so 功能比较简陋, 不过 对于我的需求来说 是足够了
可以扩展一下各个形状, 以及用户的涂鸦的控制方式[我这里为点击一下之后, 移动鼠标图形跟着鼠标走, 然后再点击一下完成一条形状的一个结点]
思路
思路 : 截图 使用robot截图, 绘制用户选中区域使用swing相关控件, 拖拽的时候 更新该控件的坐标 以及形状的绘制[GeometryUtils.java]
难点 : 1 对于swing的一些效果的掌握, 比如 全屏, 方格效果等等
2 对于用户是否更新截取部分的逻辑的处理
3 形状的绘制
4 配置文件的相关处理
几何的相关计算参考了gifGenerator, screenShotLikeQQ的哪位前辈的播客, 详见参考链接
其余部分, 请详见代码
参考代码
- Test09PointFixLike.java
package com.hx.test11;
// 实现类似于PointFix的功能
public class Test09PointFixLike extends JFrame {
// 截图需要使用的robot, 各个shape对应的factory[可配置]
// 屏幕的大小, 涂鸦面板, 当前是否处于鼠标拖拽的状态
// 开始, 退出按钮, 配置文件, 初始pathList的容量
// 读取配置得到的Map, 绘制图片的背景色, 涂鸦路径的颜色
private static Robot robot;
private static Map<String, ShapeFactory> shapeToFactory;
private Dimension screenSize;
private MyLabel label;
private boolean isDraged;
private JButton controller;
private JButton exit;
private static final String configFile = "./Test09PointFixLike.properties";
private static final int initPathCap = 2;
private static Map<String, String> configurableMap;
private Color bgColor = Color.white;
private Color shapeColor = Color.green;
// 涂鸦图案的相关配置
private int disToEnd = 12;
private int widthToLine = 20;
private int radius = 20;
private int shapeWidth = 1;
// 退出按键‘esc‘, 清除所有path的按键‘c‘, 鼠标的样式控制
// 控制窗口的大小, 默认的窗口的位置, 当前需要采用的形状
private int escCode = 27;
private int clearCode = 67;
private int cursorCode = 1;
private Dimension frameDimen = new Dimension(80, 80);
private Point framePos = new Point(0, 0);
private ShapeFactory shapeFactory = new LineWithArrowWithCircleFactory();
// 初始化robot, 读取配置
static {
try {
robot = new Robot();
} catch (AWTException e) {
e.printStackTrace();
}
shapeToFactory = new HashMap<>();
shapeToFactory.put("line", new LineFactory());
shapeToFactory.put("lineWithArrow", new LineWithArrowFactory());
shapeToFactory.put("lineWithArrowWithCircle", new LineWithArrowWithCircleFactory());
configurableMap = new LinkedHashMap<>();
configurableMap.put("bgColor", "#ffffff");
configurableMap.put("shapeColor", "#00ff00");
configurableMap.put("disToEnd", "12");
configurableMap.put("widthToLine", "20");
configurableMap.put("radius", "20");
configurableMap.put("shapeWidth", "1");
configurableMap.put("shapeFactory", "lineWithArrowWithCircle");
configurableMap.put("escCode", "27");
configurableMap.put("clearCode", "67");
configurableMap.put("cursorCode", "1");
configurableMap.put("frameDimen.width", "80");
configurableMap.put("frameDimen.height", "60");
configurableMap.put("framePos.x", "100");
configurableMap.put("framePos.y", "100");
}
// 初始化
public Test09PointFixLike() {
boolean isOk = true;
List<String> lines = null;
try {
lines = Tools.getContentWithList(configFile);
} catch (IOException e) {
isOk = false;
Log.err("error while read the configFile : " + configFile);
}
if (isOk) {
for (String line : lines) {
int idxOfEq = line.indexOf("=");
String key = line.substring(0, idxOfEq).trim();
if (configurableMap.containsKey(key)) {
configurableMap.put(key, line.substring(idxOfEq + 1).trim());
}
}
}
if (isOk) {
try {
this.bgColor = (configurableMap.get("bgColor") == null ? this.bgColor : getColor((String)configurableMap.get("bgColor")));
this.shapeColor = (configurableMap.get("shapeColor") == null ? this.shapeColor : getColor((String)configurableMap.get("shapeColor")));
this.disToEnd = (configurableMap.get("disToEnd") == null ? this.disToEnd : Integer.parseInt((String)configurableMap.get("disToEnd")));
this.widthToLine = (configurableMap.get("widthToLine") == null ? this.widthToLine : Integer.parseInt((String)configurableMap.get("widthToLine")));
this.radius = (configurableMap.get("radius") == null ? this.radius : Integer.parseInt((String)configurableMap.get("radius")));
this.shapeWidth = (configurableMap.get("shapeWidth") == null ? this.shapeWidth : Integer.parseInt((String)configurableMap.get("shapeWidth")));
this.shapeFactory = (configurableMap.get("shapeFactory") == null ? this.shapeFactory : (ShapeFactory)shapeToFactory.get(configurableMap.get("shapeFactory")));
this.escCode = (configurableMap.get("escCode") == null ? this.escCode : Integer.parseInt((String)configurableMap.get("escCode")));
this.clearCode = (configurableMap.get("clearCode") == null ? this.clearCode : Integer.parseInt((String)configurableMap.get("clearCode")));
this.cursorCode = (configurableMap.get("cursorCode") == null ? this.cursorCode : Integer.parseInt((String)configurableMap.get("cursorCode")));
this.frameDimen.width = (configurableMap.get("frameDimen.width") == null ? this.frameDimen.width : Integer.parseInt((String)configurableMap.get("frameDimen.width")));
this.frameDimen.height = (configurableMap.get("frameDimen.height") == null ? this.frameDimen.height : Integer.parseInt((String)configurableMap.get("frameDimen.height")));
this.framePos.x = (configurableMap.get("framePos.x") == null ? this.framePos.x : Integer.parseInt((String)configurableMap.get("framePos.x")));
this.framePos.y = (configurableMap.get("framePos.y") == null ? this.framePos.y : Integer.parseInt((String)configurableMap.get("framePos.y")));
} catch (Exception e) {
Log.err("error while init the configurable ‘Var‘ : " + e.getMessage());
}
}
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setResizable(false);
setUndecorated(true);
setVisible(true);
setTitle("pointFix");
setBounds(this.framePos.x, this.framePos.y, this.frameDimen.width, this.frameDimen.height);
setCursor(Cursor.getPredefinedCursor(this.cursorCode));
this.controller = new JButton("begin");
this.exit = new JButton("exit");
add(this.controller, BorderLayout.CENTER);
add(this.exit, BorderLayout.SOUTH);
this.label = new MyLabel(null);
this.label.setBounds(0, 0, getScreenSize().width, getScreenSize().height);
this.controller.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (!Test09PointFixLike.this.isDraged) {
Test09PointFixLike.this.controller.setVisible(false);
Test09PointFixLike.this.controller.setText("stop");
Test09PointFixLike.this.exit.setVisible(false);
Test09PointFixLike.this.setLocation(0, 0);
Test09PointFixLike.this.setSize(Test09PointFixLike.this.getScreenSize());
Test09PointFixLike.this.setVisible(false);
BufferedImage allScreenImg = Test09PointFixLike.robot.createScreenCapture(new Rectangle(0, 0, Test09PointFixLike.this.getScreenSize().width, Test09PointFixLike.this.getScreenSize().height));
Test09PointFixLike.this.label.setVisible(true);
Test09PointFixLike.this.label.setImage(allScreenImg);
Test09PointFixLike.this.add(Test09PointFixLike.this.label);
Test09PointFixLike.this.setVisible(true);
Test09PointFixLike.this.requestFocus();
}
}
});
this.exit.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (!Test09PointFixLike.this.isDraged) {
Test09PointFixLike.this.saveConfig();
Test09PointFixLike.this.dispose();
System.exit(0);
}
}
});
addKeyListener(new KeyListener() {
public void keyTyped(KeyEvent e) {
}
public void keyReleased(KeyEvent e) {
}
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == Test09PointFixLike.this.escCode) {
Test09PointFixLike.this.label.saveShape();
Test09PointFixLike.this.label.setVisible(false);
Test09PointFixLike.this.setLocation(Test09PointFixLike.this.framePos);
Test09PointFixLike.this.setSize(Test09PointFixLike.this.frameDimen);
Test09PointFixLike.this.exit.setVisible(true);
Test09PointFixLike.this.controller.setVisible(true);
Test09PointFixLike.this.controller.setText("start");
} else if (e.getKeyCode() == Test09PointFixLike.this.clearCode) {
Test09PointFixLike.this.label.clear();
}
}
});
MouseAdapter adapter = new DragCtrlAdapter(this);
this.controller.addMouseListener(adapter);
this.controller.addMouseMotionListener(adapter);
this.exit.addMouseListener(adapter);
this.exit.addMouseMotionListener(adapter);
MouseAdapter dragAdapter = new DragAdapter(this);
addMouseListener(dragAdapter);
addMouseMotionListener(dragAdapter);
}
// main
public static void main(String[] args) {
new Test09PointFixLike();
}
// 获取屏幕的大小
private Dimension getScreenSize() {
if (this.screenSize == null) {
this.screenSize = Toolkit.getDefaultToolkit().getScreenSize();
}
return this.screenSize;
}
// 判断两个点是否相同
private static boolean pointEquals(Point2D p1, Point2D p2) {
return (GeometryUtil.equals(p1.getX(), p2.getX())) && (GeometryUtil.equals(p1.getY(), p2.getY()));
}
// 获取给定的字符串代表的颜色
private static Color getColor(String color) {
if (color.length() < 6) {
return Color.white;
}
int start = 0;
if (color.charAt(0) == ‘#‘) {
start++;
}
int r = Integer.valueOf(color.substring(start, start+=2), 16).intValue();
int g = Integer.valueOf(color.substring(start, start+=2), 16).intValue();
int b = Integer.valueOf(color.substring(start, start+=2), 16).intValue();
return new Color(r, g, b);
}
// 持久化配置
private void saveConfig() {
StringBuilder sb = new StringBuilder();
configurableMap.put("framePos.x", String.valueOf(this.framePos.x));
configurableMap.put("framePos.y", String.valueOf(this.framePos.y));
for (Map.Entry<String, String> entry : configurableMap.entrySet())
Tools.appendCRLF(sb, entry.getKey() + " = " + entry.getValue());
try {
Tools.save(sb.toString(), configFile);
} catch (IOException e) {
Log.err("error while save the configFile !");
}
}
// 响应用户涂鸦的MouseAdapter
static class DragAdapter extends MouseAdapter {
private Test09PointFixLike parentFrame;
public DragAdapter(Test09PointFixLike parentFrame) {
this.parentFrame = parentFrame;
}
public void mousePressed(MouseEvent e) {
super.mousePressed(e);
if (e.getClickCount() > 1) {
this.parentFrame.label.saveShape();
} else {
if (this.parentFrame.label.isShapeEnd) {
this.parentFrame.label.setStart(e.getX(), e.getY());
this.parentFrame.label.setEnd(e.getX(), e.getY() + 2);
this.parentFrame.label.addPathes(parentFrame.label.start);
this.parentFrame.label.addPathes(parentFrame.label.end);
} else {
this.parentFrame.label.setEnd(e.getX(), e.getY());
this.parentFrame.label.addPathes();
}
}
}
public void mouseReleased(MouseEvent e) {
super.mouseReleased(e);
}
public void mouseMoved(MouseEvent e) {
super.mouseDragged(e);
if (! parentFrame.label.isShapeEnd)
this.parentFrame.label.setEnd(e.getX(), e.getY());
}
public void mouseDragged(MouseEvent e) {
mousePressed(e);
}
}
// 拖拽相关的MouseAdapter
static class DragCtrlAdapter extends DragMouseAdapter {
private Test09PointFixLike parentFrame;
public DragCtrlAdapter(Test09PointFixLike parentFrame) {
super(parentFrame);
this.parentFrame = parentFrame;
}
public void mousePressed(MouseEvent e) {
super.mousePressed(e);
this.parentFrame.isDraged = false;
this.parentFrame.framePos.setLocation(this.parentFrame.getLocation());
}
public void mouseReleased(MouseEvent e) {
super.mouseReleased(e);
}
public void mouseDragged(MouseEvent e) {
super.mouseDragged(e);
this.parentFrame.isDraged = true;
}
}
// shape 相关
static abstract class Shape {
protected List<Point> pathes;
protected int width;
public Shape(List<Point> pathes, int width) {
this.pathes = pathes;
this.width = width;
}
public void setPathes(List<Point> pathes) {
this.pathes = pathes;
}
public static final void drawLine(Graphics g, Point start, Point end, int width) {
g.drawLine(start.x, start.y, end.x, end.y);
Point startVerticlePoint = GeometryUtil.toPoint(GeometryUtil.verticalPoint(start, end));
Point endVerticlePoint = GeometryUtil.toPoint(GeometryUtil.verticalPoint(end, start));
for (int i = 1; i < width; i++) {
Point startTmp = GeometryUtil.toPoint(GeometryUtil.extentPoint(start, startVerticlePoint, i));
Point endTmp = GeometryUtil.toPoint(GeometryUtil.extentPoint(end, endVerticlePoint, i));
g.drawLine(startTmp.x, startTmp.y, endTmp.x, endTmp.y);
startTmp = GeometryUtil.toPoint(GeometryUtil.extentPoint(start, startVerticlePoint, -i));
endTmp = GeometryUtil.toPoint(GeometryUtil.extentPoint(end, endVerticlePoint, -i));
g.drawLine(startTmp.x, startTmp.y, endTmp.x, endTmp.y);
}
}
public abstract void draw(Graphics paramGraphics);
}
static class Line extends Shape {
public Line(List<Point> pathes, int width) {
super(pathes, width);
}
public void draw(Graphics g) {
for (int i = 1; i < this.pathes.size(); i++) {
Point start = (Point)this.pathes.get(i - 1); Point end = (Point)this.pathes.get(i);
drawArrow(g, start, end, i - 1);
}
}
protected void drawArrow(Graphics g, Point start, Point end, int idx) {
Test09PointFixLike.Shape.drawLine(g, start, end, this.width);
}
}
static class LineWithArrow extends Test09PointFixLike.Line {
protected int disToEnd;
protected int widthToLine;
public LineWithArrow(List<Point> pathes, int width, int disToEnd, int widthToLine) {
super(pathes, width);
this.disToEnd = disToEnd;
this.widthToLine = widthToLine;
}
protected void drawArrow(Graphics g, Point start, Point end, int idx) {
drawLine(g, start, end, this.width);
Point crossPoint = GeometryUtil.toPoint(GeometryUtil.extentPoint(end, start, this.disToEnd));
Point upPoint = GeometryUtil.toPoint(GeometryUtil.extentPoint(crossPoint, GeometryUtil.verticalPoint(crossPoint, end), this.widthToLine));
Point downPoint = GeometryUtil.toPoint(GeometryUtil.extentPoint(crossPoint, upPoint, -this.widthToLine));
drawLine(g, upPoint, downPoint, this.width);
drawLine(g, end, upPoint, this.width);
drawLine(g, end, downPoint, this.width);
}
}
static class LineWithArrowWithCircle extends Test09PointFixLike.LineWithArrow {
protected int radius;
public LineWithArrowWithCircle(List<Point> pathes, int width, int disToEnd, int widthToLine, int radius) {
super(pathes, width, disToEnd, widthToLine);
this.radius = radius;
}
public void drawArrow(Graphics g, Point start, Point end, int idx) {
super.drawArrow(g, start, end, idx);
Point crossPoint = GeometryUtil.toPoint(GeometryUtil.extentPoint(end, start, this.disToEnd));
Point center = GeometryUtil.toPoint(GeometryUtil.extentPoint(crossPoint, end, -(this.radius >> 1)));
g.drawOval(center.x - (this.radius >> 1), center.y - (this.radius >> 1), this.radius, this.radius);
}
}
// factory 相关
static abstract class ShapeFactory {
public abstract Test09PointFixLike.Shape createShape(List<Point> paramList, int[] paramArrayOfInt);
}
static class LineFactory extends Test09PointFixLike.ShapeFactory {
public Test09PointFixLike.Shape createShape(List<Point> pathes, int[] args) {
int width = args[0];
return new Test09PointFixLike.Line(pathes, width);
}
}
static class LineWithArrowFactory extends Test09PointFixLike.ShapeFactory {
public Test09PointFixLike.Shape createShape(List<Point> pathes, int[] args) {
int width = args[0]; int distToEnd = args[1]; int widthToLine = args[2];
return new Test09PointFixLike.LineWithArrow(pathes, width, distToEnd, widthToLine);
}
}
static class LineWithArrowWithCircleFactory extends Test09PointFixLike.ShapeFactory {
public Test09PointFixLike.Shape createShape(List<Point> pathes, int[] args) {
int width = args[0]; int distToEnd = args[1]; int widthToLine = args[2]; int radius = args[3];
return new Test09PointFixLike.LineWithArrowWithCircle(pathes, width, distToEnd, widthToLine, radius);
}
}
// 整个涂鸦画面对应的Label
class MyLabel extends JLabel {
private Image allScreenImg;
private List<Point> pathes = new ArrayList<>();
private Point start = new Point(); private Point end = new Point();
private List<Test09PointFixLike.Shape> shapes = new ArrayList<>(initPathCap);
private boolean isShapeEnd = true;
public MyLabel(Image allScreenImg) {
this.allScreenImg = allScreenImg;
}
public void setStart(Point p) {
setStart(p.x, p.y);
}
public void setStart(int x, int y) {
this.start.setLocation(x, y);
}
public void setEnd(Point p) {
setEnd(p.x, p.y);
}
public void setEnd(int x, int y) {
this.end.setLocation(x, y);
repaint();
}
public void setImage(Image allScreenImg) {
this.allScreenImg = allScreenImg;
repaint();
}
public void addPathes() {
Point nextTmpPoint = GeometryUtil.toPoint(GeometryUtil.extentPoint(this.end, this.start, -8.0D));
this.start = this.end; this.end = new Point(nextTmpPoint);
addPathes(this.end);
}
public void addPathes(Point p) {
Log.log("add point : " + p + ", at : " + new Date().toString());
this.pathes.add(p);
if (this.isShapeEnd) {
Test09PointFixLike.Shape shape = Test09PointFixLike.this.shapeFactory.createShape(this.pathes, new int[] { Test09PointFixLike.this.shapeWidth, Test09PointFixLike.this.disToEnd, Test09PointFixLike.this.widthToLine, Test09PointFixLike.this.radius } );
this.shapes.add(shape);
this.isShapeEnd = false;
}
repaint();
}
public void saveShape() {
if (this.pathes.size() > 0) {
this.pathes.remove(this.pathes.size() - 1);
}
Log.log("finished a shape : " + this.pathes.toString() + ", at : " + new Date().toString());
this.isShapeEnd = true;
this.pathes = new LinkedList<>();
this.start = new Point(); this.end = new Point();
repaint();
}
public void clear() {
this.pathes.clear();
this.shapes.clear();
saveShape();
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (this.allScreenImg != null) {
g.drawImage(this.allScreenImg, 0, 0, this);
}
drawShapes(g);
Graphics2D g2d = (Graphics2D)g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setColor(Test09PointFixLike.this.bgColor);
AlphaComposite composite = AlphaComposite.getInstance(3, 0.4F);
g2d.setComposite(composite);
g2d.fill(new RoundRectangle2D.Float(0.0F, 0.0F, getWidth(), getHeight(), 0.0F, 0.0F));
}
private void drawShapes(Graphics g) {
Color prevColor = g.getColor();
g.setColor(Test09PointFixLike.this.shapeColor);
for(Test09PointFixLike.Shape shape : this.shapes) {
shape.draw(g);
}
g.setColor(prevColor);
}
}
}
- GeometryUtil.java [此类来自于gifGenerator, screenShotLikeQQ参考链接中的哪位作者的播客, 我在原作者的基础上面做了一定程度的更改]
/**
* file name : GeometryUtil.java
* created at : 2:12:59 PM Nov 26, 2015
* created by 970655147
*/
package com.hx.util;
// 几何相关计算
public class GeometryUtil {
// test
public static void main(String[] args) {
// Point2D p = rotate(50, 0, 30);
Point2D p = rotate(43.30127018922194, 24.999999999999996, 60);
Log.log(p );
check(p );
p = rotate(0, 0, 10, 10, 90);
Log.log(p );
check(p );
}
// check
private static void check(Point2D p) {
Log.log("dis to ‘zero‘ : " + distanceOfPoints(defaultZero, p) );
}
// 计算p1, p2之间的距离
public static double distanceOfPoints(double x1, double y1, double x2, double y2) {
double disX = x2 - x1;
double disY = y2 - y1;
return Math.sqrt(disX * disX + disY * disY);
}
public static double distanceOfPoints(Point2D p1, Point2D p2) {
return distanceOfPoints(p1.getX(), p1.getY(), p2.getX(), p2.getY() );
}
// 计算p1, p2的中点
public static Point2D middlePoint(double x1, double y1, double x2, double y2) {
double x = (x1 + x2) / 2;
double y = (y1 + y2) / 2;
return new Point2D.Double(x, y);
}
public static Point2D middlePoint(Point2D p1, Point2D p2) {
return middlePoint(p1.getX(), p1.getY(), p2.getX(), p2.getY() );
}
// 沿着p1 -> p2方向上距离p1为disToStartPoint的点
public static Point2D extentPoint(double x1, double y1, double x2, double y2, double disToStartPoint) {
double dis = distanceOfPoints(x1, y1, x2, y2);
double sin = (y2 - y1) / dis;
double cos = (x2 - x1) / dis;
double deltaX = disToStartPoint * cos;
double deltaY = disToStartPoint * sin;
return new Point2D.Double(x1 + deltaX, y1 + deltaY);
}
public static Point2D extentPoint(Point2D p1, Point2D p2, double disToStartPoint) {
return extentPoint(p1.getX(), p1.getY(), p2.getX(), p2.getY(), disToStartPoint );
}
// 将p1, p2以p1为基准逆时针反转90度
// e‘->|
// ----- => |
// /|\ /|\ |
// | | |
// s e s-> |
public static Point2D verticalPoint(Point2D p1, Point2D p2) {
return verticalPoint(p1.getX(), p1.getY(), p2.getX(), p2.getY() );
}
public static Point2D verticalPoint(double x1, double y1, double x2, double y2) {
return rotate(x1, y1, x2, y2, 90);
}
// 默认的原点坐标[没指明基准点, 默认以远点为基准点]
public final static Point2D defaultZero = new Point2D.Double(0, 0);
// 如果p1, p2的y坐标相同, 则直接旋转degree度
// 否则, 将两个点的y坐标以p1的y坐标为基准, 旋转degree + ‘(p1 -> p2)相对于x轴的度数‘
public static Point2D rotate(double x1, double y1, double x2, double y2, double degree) {
// Log.log(y1, y2);
// Log.log("degree : " + degree);
double dis = distanceOfPoints(x1, y1, x2, y2);
if(! equals(y1, y2) ) {
// 通过tan计算度数 (tan) => Math.toDegrees(Math.atan(tan ))
return rotate(x1, y1, x1+dis, y1, degree + Math.toDegrees(Math.atan((y2 - y1) / (x2 - x1)) ) );
}
double radians = Math.toRadians(degree);
double sin = Math.sin(radians );
double cos = Math.cos(radians );
double offX = dis * cos;
double offY = dis * sin;
return new Point2D.Double(x1 + offX, y1 + offY);
}
public static Point2D rotate(Point2D p1, Point2D p2, double degree) {
return rotate(p1.getX(), p1.getY(), p2.getX(), p2.getY(), degree);
}
// 将p1以远点为基准旋转degree度 [下面的注释是原作者的, 是关于如何计算旋转给定的结点的数学推导]
// cosθ -sinθ 0
// sinθ +conθ 0
// 0000 +0000 1
// x = r*cosα, y = r*sinα
// x‘ = r*cos(α+θ) = r*cosα*cosθ - r*sinα*sinθ = x*cosθ - y*sinθ
// y‘ = r*sin(α+θ) = r*sinα*cosθ + r*cosα*sinθ = x*sinθ + y*cosθ
// (x, y)绕圆心旋转degree度
public static Point2D rotate(double x, double y, double degree) {
return rotate(defaultZero.getX(), defaultZero.getY(), x, y, degree);
}
public static Point2D rotate(Point2D p, double degree) {
return rotate(p.getX(), p.getY(), degree);
}
// 将两个double视为相同的差值的阈值[浮点区间判定]
public final static double minOff = 0.0001d;
// 判定两个浮点数是否相同
public static boolean equals(double d1, double d2) {
return Math.abs(d1 - d2) < minOff;
}
// 将Point2D转换为Point
public static java.awt.Point toPoint(Point2D p2d) {
return new java.awt.Point((int) p2d.getX(), (int) p2d.getY() );
}
}
效果截图
配置说明
总结
小工具, 扯扯淡, 向原作者致敬 !
参考 : 1[GeometryUtil] http://blog.csdn.net/ycb1689/article/details/7656171
注 : 因为作者的水平有限,必然可能出现一些bug, 所以请大家指出!