spacebox/src/Segment.cpp

164 lines
3.9 KiB
C++

#include "Segment.hpp"
Segment::Segment() : Segment({0, 0}, {0, 0}) {};
Segment::Segment(const glm::vec2& location) : Segment(location, location) {};
Segment::Segment(const glm::vec2& start, const glm::vec2& end) : start(start), end(end) {};
glm::vec2 Segment::get_start() const
{
return start;
}
void Segment::set_start(const glm::vec2& s)
{
start = s;
}
glm::vec2 Segment::get_end() const
{
return end;
}
void Segment::set_end(const glm::vec2& e)
{
end = e;
}
// taken from http://www.realtimerendering.com/resources/GraphicsGems/gemsii/xlines.c
bool Segment::intersect(const Segment& segment, glm::vec2* intersection) const
{
float x1 = start.x, y1 = start.y, x2 = end.x, y2 = end.y, x3 = segment.start.x,
y3 = segment.start.y, x4 = segment.end.x, y4 = segment.end.y;
float a1, a2, b1, b2, c1, c2; // Coefficients of line eqns.
float r1, r2, r3, r4; // 'Sign' values
float denom, num; // Intermediate values
// Compute a1, b1, c1, where line joining points 1 and 2 is "a1 x + b1 y + c1 = 0"
a1 = y2 - y1;
b1 = x1 - x2;
c1 = x2 * y1 - x1 * y2;
// Compute r3 and r4
r3 = a1 * x3 + b1 * y3 + c1;
r4 = a1 * x4 + b1 * y4 + c1;
// Check signs of r3 and r4. If both point 3 and point 4 lie on same side of
// line 1, the line segments do not intersect
if (r3 != 0 && r4 != 0 && std::copysign(1, r3) == std::copysign(1, r4))
{
return false;
}
// Compute a2, b2, c2
a2 = y4 - y3;
b2 = x3 - x4;
c2 = x4 * y3 - x3 * y4;
// Compute r1 and r2
r1 = a2 * x1 + b2 * y1 + c2;
r2 = a2 * x2 + b2 * y2 + c2;
// Check signs of r1 and r2. If both point 1 and point 2 lie on same side
// of second line segment, the line segments do not intersect
if (r1 != 0 && r2 != 0 && std::copysign(1, r1) == std::copysign(1, r2))
{
return false;
}
// Line segments intersect: compute intersection point
denom = a1 * b2 - a2 * b1;
if (denom == 0)
{
return false;
}
num = b1 * c2 - b2 * c1;
if (intersection != NULL)
{
intersection->x = num / denom;
}
num = a2 * c1 - a1 * c2;
if (intersection != NULL)
{
intersection->y = num / denom;
}
return true;
}
bool Segment::intersect(const Segment& segment, glm::vec2& intersection) const
{
return intersect(segment, &intersection);
}
float Segment::get_dx() const
{
return end.x - start.x;
}
float Segment::get_dy() const
{
return end.y - start.y;
}
float Segment::get_length() const
{
return glm::distance(start, end);
}
Box Segment::get_box() const
{
float x = std::min(start.x, end.x);
float y = std::min(start.y, end.y);
float w = std::abs(get_dx());
float h = std::abs(get_dy());
return Box({x, y}, {w, h});
}
void Segment::move(const glm::vec2& delta)
{
start += delta;
end += delta;
}
glm::vec2 Segment::get_center() const
{
return get_subsegments(2)[0].end;
}
glm::vec2 Segment::get_step(float speed) const
{
float angle = glm::atan(end.x - start.x, end.y - start.y);
return glm::vec2(speed * glm::sin(angle), speed * glm::cos(angle));
}
glm::vec2 Segment::get_step_relative(float relative_length_per_step) const
{
return get_step(glm::distance(start, end) * relative_length_per_step);
}
std::vector<Segment> Segment::get_subsegments(int count) const
{
glm::vec2 step = get_step_relative(1.0f / count);
std::vector<Segment> subsegments;
subsegments.reserve(count);
glm::vec2 subsegment_start = start, subsegment_end;
for (int ii = 0; ii < count; ii++)
{
subsegment_end = subsegment_start + step;
subsegments.emplace_back(subsegment_start, subsegment_end);
subsegment_start = subsegment_end;
}
return subsegments;
}
std::ostream& operator<<(std::ostream& out, const Segment& segment)
{
out << "{(" << segment.start.x << ", " << segment.start.y << "), (" <<
segment.end.x << ", " << segment.end.y << ")}";
return out;
}