前言:代码全部实现可视化,能够带来更直观的体验,java语言。
1:判断点是否在多边形内
这个问题最初遇到是我在毕设的游戏设计地图时候,如何防止玩家走出边界。
思想可以参考 https://blog.csdn.net/u011722133/article/details/52813374
就是通过计算一条射线和多边形的边相交点数判断。
效果图如下,当然也可以实现任意的多边形。
package Tools;
import java.awt.*;
import java.util.concurrent.TimeUnit;
import javax.swing.*;
import org.apache.commons.lang.math.RandomUtils;
public class RandomPoint extends JPanel {
public static JFrame jFrame;
public static int JFRAME_WIDTH = 1300;
public static int JFRAME_HEIGHT = 950;
static boolean first = true;
int[][] map = {{-1561, 162}, {-891, 619}, {-351, 277}, {-1013, -195}};
public RandomPoint() {
jFrame = new JFrame();
Dimension ScreenSize = Toolkit.getDefaultToolkit().getScreenSize();
int JFrame_X = (int) (ScreenSize.getWidth() - JFRAME_WIDTH) / 2;
int JFrame_Y = (int) (ScreenSize.getHeight() - JFRAME_HEIGHT) / 2;
jFrame.setLocation(JFrame_X, JFrame_Y);
jFrame.setSize(JFRAME_WIDTH, JFRAME_HEIGHT);
jFrame.setLayout(null);
this.setBounds(0, 0, JFRAME_WIDTH, JFRAME_HEIGHT);
this.setLayout(null);
jFrame.add(this);
jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jFrame.setVisible(true);
new Thread(() -> {
for (; ; ) {
try {
TimeUnit.MILLISECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.repaint();
}
}).start();
}
public static void main(String[] args) {
new RandomPoint();
}
@Override
public void paint(Graphics g) {
int M = 1600;
int N = 200;
if (first) {
g.setColor(Color.GREEN);
((Graphics2D) g).setStroke(new BasicStroke(5.0f));
for (int i = 0, j = 3; i < 4; j = i++) {
g.drawLine(map[i][0] + M, map[i][1] + N, map[j][0] + M, map[j][1] + N);
}
first = false;
}
g.setColor(Color.RED);
int w = RandomUtils.nextInt(JFRAME_WIDTH);
int h = RandomUtils.nextInt(JFRAME_HEIGHT);
while (!map_collision(w - M, h - N)) {
w = RandomUtils.nextInt(JFRAME_WIDTH);
h = RandomUtils.nextInt(JFRAME_HEIGHT);
}
g.fillOval(w, h, 20, 20);
}
public boolean map_collision(float testx, float testy) {
int[][] ver = map;
int nvert = map.length;
int i, j;
boolean c = false;
for (i = 0, j = nvert - 1; i < nvert; j = i++) {
if (((ver[i][1] > testy) != (ver[j][1] > testy)) && (testx < (ver[j][0] - ver[i][0]) * (testy - ver[i][1]) / (ver[j][1] - ver[i][1]) + ver[i][0]))
c = !c;
}
return c;
}
}
2:二维矩阵的旋转
思想可以参考:https://baike.baidu.com/item/%E6%97%8B%E8%BD%AC%E7%9F%A9%E9%98%B5 里的二维空间矩阵计算。
高中知识体系
效果如下
package Tools;
import java.awt.*;
import java.util.concurrent.TimeUnit;
import javax.swing.*;
/**
* 旋转的矩阵
*/
public class RotationMatrix extends JPanel {
public static JFrame jFrame;
public static int JFRAME_WIDTH = 818;
public static int JFRAME_HEIGHT = 848;
double degrees = 0.0;
public RotationMatrix() {
jFrame = new JFrame();
Dimension ScreenSize = Toolkit.getDefaultToolkit().getScreenSize();
int JFrame_X = (int) (ScreenSize.getWidth() - JFRAME_WIDTH) / 2;
int JFrame_Y = (int) (ScreenSize.getHeight() - JFRAME_HEIGHT) / 2;
jFrame.setLocation(JFrame_X, JFrame_Y);
jFrame.setSize(JFRAME_WIDTH, JFRAME_HEIGHT);
jFrame.setLayout(null);
this.setBounds(0, 0, JFRAME_WIDTH, JFRAME_HEIGHT);
this.setLayout(null);
jFrame.add(this);
jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jFrame.setVisible(true);
new Thread(() -> {
for (; ; ) {
try {
TimeUnit.MILLISECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
degrees += 0.05;
this.repaint();
}
}).start();
}
public static void main(String[] args) {
new RotationMatrix();
}
@Override
public void paint(Graphics g) {
super.paint(g);
g.setColor(Color.RED);
int m = 10;
for (int i = 0; i < m; i++) {
for (int j = 0; j < m; j++) {
double x = i * 30.0;
double y = j * 30.0;
double _x = x * Math.cos(Math.toRadians(degrees)) - y * Math.sin(Math.toRadians(degrees));
double _y = x * Math.sin(Math.toRadians(degrees)) + y * Math.cos(Math.toRadians(degrees));
g.fillOval(400 + (int) _x, 400 + (int) _y, 10, 10);
}
}
}
}
3:比较难的三维矩阵在二维上的投影,可旋转
投影的计算步骤是:
1:三维坐标的旋转 参考:https://baike.baidu.com/item/%E6%97%8B%E8%BD%AC%E7%9F%A9%E9%98%B5 中的三维矩阵
2:计算三维坐标与平面的交点,求得投影平面的三维坐标 参考:https://blog.csdn.net/abcjennifer/article/details/6688080
3:投影平面的三维坐标转换成二维坐标,已知投影平面的三维中心点,两个坐标可以算得,在xy方向的距离
4:单得到距离还差方向,就是进行投影后的xy方向的比较。
说明
代码实现上尽可能减少重复的计算,设计了很多了缓存计算量的变量,包括枚举类的设计,在视图上没有处理好不可见点和线的隐藏。
效果如下:
看代码吧
package Tools;
import java.awt.*;
import java.util.concurrent.TimeUnit;
import javax.swing.*;
public class Matrix_3D extends JPanel {
public static JFrame jFrame;
static double length = 300.0;
static double[][] points = {{-length, -length, length}, {length, -length, length}, {length, length, length}, {-length, length, length},
{-length, -length, -length}, {length, -length, -length}, {length, length, -length}, {-length, length, -length}};
//投影平面的中心点
static double[] canvas = {0, 500, 500};
//投影平面的法向量
static double[] canvas_c = {0, 100, 100};
//投影平面的上的一点,此点到canvas视为投影平面的最上方
static double[] canvas_p = {0, 400, 600};
//摄像机位置
static double[] eye = {0, 600, 600};
//计算结果缓存
double[][] d = new double[8][3];
int[][] b = new int[8][2];
int center_x = 500;
int center_y = 500;
int JFRAME_WIDTH = 1000;
int JFRAME_HEIGHT = 1000;
enum XYZ {
X(0, 0),
Y(0, 0),
Z(0, 0.1);
private final double change;
private double degree;
double sin;
double cos;
XYZ(double init, double change) {
this.degree = init;
this.change = change;
}
public void init() {
double radians = Math.toRadians(degree);
sin = Math.sin(radians);
cos = Math.cos(radians);
}
public void change() {
degree = (degree + change) % 360;
init();
}
}
public Matrix_3D() {
jFrame = new JFrame();
Dimension ScreenSize = Toolkit.getDefaultToolkit().getScreenSize();
int JFrame_X = (int) (ScreenSize.getWidth() - JFRAME_WIDTH) / 2;
int JFrame_Y = (int) (ScreenSize.getHeight() - JFRAME_HEIGHT) / 2;
jFrame.setLocation(JFrame_X, JFrame_Y);
jFrame.setSize(JFRAME_WIDTH, JFRAME_HEIGHT);
jFrame.setLayout(null);
this.setBounds(0, 0, JFRAME_WIDTH, JFRAME_HEIGHT);
this.setLayout(null);
jFrame.add(this);
jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jFrame.setVisible(true);
XYZ.X.init();
XYZ.Y.init();
XYZ.Z.init();
new Thread(() -> {
for (; ; ) {
try {
TimeUnit.MILLISECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
XYZ.X.change();
XYZ.Y.change();
XYZ.Z.change();
this.repaint();
}
}).start();
}
public static void main(String[] args) {
new Matrix_3D();
}
@Override
public void paint(Graphics g) {
super.paint(g);
for (int i = 0; i < 8; i++) {
get(i);
}
paint___(g, 0, 1);
paint___(g, 1, 2);
paint___(g, 2, 3);
paint___(g, 3, 0);
paint___(g, 0, 4);
paint___(g, 1, 5);
paint___(g, 2, 6);
paint___(g, 3, 7);
paint___(g, 4, 5);
paint___(g, 5, 6);
paint___(g, 6, 7);
paint___(g, 7, 4);
}
public void paint___(Graphics g, int i, int j) {
g.drawLine(center_x + b[i][0], center_y + b[i][1], center_x + b[j][0], center_y + b[j][1]);
}
public void get(int index) {
d[index][0] = points[index][0];
d[index][1] = points[index][1] * XYZ.X.cos - points[index][2] * XYZ.X.sin;
d[index][2] = points[index][1] * XYZ.X.sin + points[index][2] * XYZ.X.cos;
double qwe = d[index][0] * XYZ.Y.cos + d[index][2] * XYZ.Y.sin;
d[index][2] = -d[index][0] * XYZ.Y.sin + d[index][2] * XYZ.Y.cos;
double rty = qwe * XYZ.Z.cos - d[index][1] * XYZ.Z.sin;
d[index][1] = qwe * XYZ.Z.sin + d[index][1] * XYZ.Z.cos;
d[index][0] = rty;
double[] t = aaaaCalPlaneLineIntersectPoint(d[index]);
if (t == null) {
return;
}
double o = gerDistance(t, canvas_p);
double p = gerDistance(canvas_p, canvas);
double i = gerDistance(t, canvas);
double ii = Math.sqrt(i);
double cosA = Math.abs((i + p - o) / (2 * ii * Math.sqrt(p)));
double sina = Math.sqrt((1 - Math.pow(cosA, 2)));
double xx = ii * sina * (t[0] >= canvas[0] ? 1 : -1);
double yy = ii * cosA * (t[1] >= canvas[1] ? 1 : -1);
b[index][0] = (int) xx;
b[index][1] = (int) yy;
}
private double[] aaaaCalPlaneLineIntersectPoint(double[] linePoint) {
double[] returnResult = new double[3];
double vp1, vp2, vp3, n1, n2, n3, v1, v2, v3, m1, m2, m3, t, vpt;
// 平面的法线向量
vp1 = canvas_c[0];
vp2 = canvas_c[1];
vp3 = canvas_c[2];
// 平面经过的一点坐标
n1 = canvas[0];
n2 = canvas[1];
n3 = canvas[2];
// 直线的方向向量
v1 = eye[0] - linePoint[0];
v2 = eye[1] - linePoint[1];
v3 = eye[2] - linePoint[2];
// 直线经过的一点坐标
m1 = eye[0];
m2 = eye[1];
m3 = eye[2];
vpt = v1 * vp1 + v2 * vp2 + v3 * vp3;
if (vpt == 0) {
returnResult = null;
} else {
t = ((n1 - m1) * vp1 + (n2 - m2) * vp2 + (n3 - m3) * vp3) / vpt;
returnResult[0] = m1 + v1 * t;
returnResult[1] = m2 + v2 * t;
returnResult[2] = m3 + v3 * t;
}
return returnResult;
}
public double gerDistance(double[] a, double[] b) {
double i = 0;
for (int j = 0; j < 3; j++) {
i += Math.pow(Math.abs(b[j] - a[j]), 2);
}
return i;
}
}