[C++] 线段碰撞

今天一时兴起,打算写个2D的布料模拟。
思索了一番布料节点碰撞,发现可能会发生线段穿模。
然后就写了这个。

原理

其实很简单
求出线段的函数解析式

struct LinearFunction
{
	float k;
	float b;
};

LinearFunction calcFunction()
{
	LinearFunction lf;
	lf.k = (vertex0.y - vertex1.y) / (vertex0.x - vertex1.x);
	lf.b = vertex0.y - vertex0.x * lf.k;
	return lf;
}

求出交点

sf::Vector2f calcIntersection(LinearFunction lf0, LinearFunction lf1)
{
	sf::Vector2f intersection;
	intersection.x = (lf1.b - lf0.b) / (lf0.k - lf1.k);
	intersection.y = lf0.k * intersection.x + lf0.b;
	return intersection;
}

判断交点的X坐标(或Y坐标)是否在两个线段之间

float x = intersection.x;

float x000 = ls0.vertex0.x;
float x010 = ls0.vertex1.x;
float x100 = ls1.vertex0.x;
float x110 = ls1.vertex1.x;

float x00 = std::min(x000, x010);
float x01 = std::max(x000, x010);
float x10 = std::min(x100, x110);
float x11 = std::max(x100, x110);

return (x > x00 && x < x01) && (x > x10 && x < x11);

返回的值就是碰撞结果

代码

拿SFML写的

#include <SFML/Graphics.hpp>
#include <iostream>
#include <cmath>

const float RADIUS = 5.0f;

struct LinearFunction
{
	float k;
	float b;
};

class LineSegment
{
	public:
		sf::Vector2f vertex0;
		sf::Vector2f vertex1;
		sf::CircleShape vcir0;
		sf::CircleShape vcir1;

	public:
		LineSegment()
		{
			vcir0 = sf::CircleShape();
			vcir1 = sf::CircleShape();
			vcir0.setRadius(RADIUS);
			vcir1.setRadius(RADIUS);
			vcir0.setFillColor(sf::Color::Green);
			vcir1.setFillColor(sf::Color::Green);
			vcir0.setOrigin(sf::Vector2f(RADIUS, RADIUS));
			vcir1.setOrigin(sf::Vector2f(RADIUS, RADIUS));
		}

		LinearFunction calcFunction()
		{
			LinearFunction lf;
			lf.k = (vertex0.y - vertex1.y) / (vertex0.x - vertex1.x);
			lf.b = vertex0.y - vertex0.x * lf.k;
			return lf;
		}
		LineSegment& draw(sf::RenderTarget& target)
		{
			sf::VertexArray va(sf::PrimitiveType::Lines, 2);
			va[0].position = vertex0;
			va[0].color = sf::Color::Red;
			va[1].position = vertex1;
			va[1].color = sf::Color::Red;
			target.draw(va);

			vcir0.setPosition(vertex0);
			vcir1.setPosition(vertex1);
			target.draw(vcir0);
			target.draw(vcir1);
			return *this;
		}
};

sf::Vector2f calcIntersection(LinearFunction lf0, LinearFunction lf1)
{
	sf::Vector2f intersection;
	intersection.x = (lf1.b - lf0.b) / (lf0.k - lf1.k);
	intersection.y = lf0.k * intersection.x + lf0.b;
	return intersection;
}

bool isCollision(LineSegment ls0, LineSegment ls1)
{
	LinearFunction lf0 = ls0.calcFunction();
	LinearFunction lf1 = ls1.calcFunction();
	sf::Vector2f intersection = calcIntersection(lf0, lf1);

	float x = intersection.x;

	float x000 = ls0.vertex0.x;
	float x010 = ls0.vertex1.x;
	float x100 = ls1.vertex0.x;
	float x110 = ls1.vertex1.x;

	float x00 = std::min(x000, x010);
	float x01 = std::max(x000, x010);
	float x10 = std::min(x100, x110);
	float x11 = std::max(x100, x110);

	return (x > x00 && x < x01) && (x > x10 && x < x11);
}

bool isPressedVertex(sf::Vector2f pos, sf::Vector2f vertex)
{
	float deltaX = pos.x - vertex.x;
	float deltaY = pos.y - vertex.y;
	float deltaX2 = deltaX * deltaX;
	float deltaY2 = deltaY * deltaY;
	float sum = deltaX2 + deltaY2;
	float distance = std::sqrt(sum);
	return (distance <= RADIUS);
}

sf::Vector2f toVector2f(sf::Vector2i vec)
{
	return sf::Vector2f(float(vec.x), float(vec.y));
}

sf::Vector2f getMousePosition()
{
	return toVector2f(sf::Mouse::getPosition());
}

sf::Vector2f getMousePosition(sf::Window& window)
{
	return toVector2f(sf::Mouse::getPosition(window));
}

int main()
{
	sf::RenderWindow window(sf::VideoMode(640, 480), "Line Segment");
	window.setFramerateLimit(60);
	window.setVerticalSyncEnabled(true);

	sf::Vector2f* targetVertex = nullptr;

	sf::Font font;
	font.loadFromFile("simsun.ttc");

	sf::Text text;
	text.setString("not collision");
	text.setFillColor(sf::Color::Blue);
	text.setPosition(sf::Vector2f(0, 0));
	text.setFont(font);

	LineSegment ls1;
	ls1.vertex0 = sf::Vector2f(200, 200);
	ls1.vertex1 = sf::Vector2f(400, 400);

	LineSegment ls2;
	ls2.vertex0 = sf::Vector2f(200, 400);
	ls2.vertex1 = sf::Vector2f(400, 200);

	text.setString((isCollision(ls1, ls2) ? "collision" : "not collision"));

	sf::Event event;
	while (window.isOpen())
	{
		while (window.pollEvent(event))
		{
			if (event.type == sf::Event::Closed)
			{
				window.close();
			}
			else if (event.type == sf::Event::MouseButtonPressed)
			{
				sf::Vector2f pos = getMousePosition(window);

				if (isPressedVertex(pos, ls1.vertex0))
				{
					targetVertex = &ls1.vertex0;
					std::cout << "ls1.vertex0" << std::endl;
				}
				else if (isPressedVertex(pos, ls1.vertex1))
				{
					targetVertex = &ls1.vertex1;
					std::cout << "ls1.vertex1" << std::endl;
				}
				else if (isPressedVertex(pos, ls2.vertex0))
				{
					targetVertex = &ls2.vertex0;
					std::cout << "ls2.vertex0" << std::endl;
				}
				else if (isPressedVertex(pos, ls2.vertex1))
				{
					targetVertex = &ls2.vertex1;
					std::cout << "ls2.vertex1" << std::endl;
				}
			}
			else if (event.type == sf::Event::MouseMoved)
			{
				if (targetVertex != nullptr)
				{
					*targetVertex = getMousePosition(window);
				}

				text.setString((isCollision(ls1, ls2) ? "collision" : "not collision"));
			}
			else if (event.type == sf::Event::MouseButtonReleased)
			{
				targetVertex = nullptr;
			}
		}

		window.clear();
		window.draw(text);
		ls1.draw(window);
		ls2.draw(window);
		window.display();
	}
	return 0;
}

运行结果

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值