数学在绘图中的应用可视化(判断点是否在多边形内,矩阵的二维旋转,矩阵的三维旋转)

前言:代码全部实现可视化,能够带来更直观的体验,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;
	}

}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值