#include #include "vec.hpp" namespace geometry { template constexpr inline T line_plane_intersection_d(const vec& plane_point, // p0 const vec& plane_normal, // n const vec& line_start, // l0 const vec& line_vector // l ) { const T intersection = // d dot(plane_point - line_start, plane_normal) / dot(line_vector, plane_normal); return intersection; } template constexpr inline vec line_plane_intersection(const vec& plane_point, // p0 const vec& plane_normal, // n const vec& line_start, // l0 const vec& line_end ) { const auto line_vector = line_end - line_start; // l const T intersection = line_plane_intersection_d(plane_point, plane_normal, line_start, line_vector); // d return line_start + line_vector * intersection; } template constexpr inline std::tuple, vec> line_plane_intersection_two_lines(const vec& plane_point, // p0 const vec& plane_normal, // n const vec& line_start, // l0 const vec& line_end, const vec& line2_start, const vec& line2_end ) { /* it is assumed that line and line2 are the same line, but in different coordinates spaces. It is therefore possible to re-use the same interpolation value on either line vector. */ const auto line_vector = line_end - line_start; // l const auto line2_vector = line2_end - line2_start; const T intersection = line_plane_intersection_d(plane_point, plane_normal, line_start, line_vector); // d return { line_start + line_vector * intersection , line2_start + line2_vector * intersection }; } template T clip_boundary(const vec& plane_point, // X const vec& plane_normal, // Nc const vec& line_point ) { return dot(plane_normal, line_point - plane_point); } template inline T positive_modulo(T a, T b) { return (a % b + b) % b; } template inline int clip_polygon1(vec * output, const vec plane_point, const vec plane_normal, const vec * polygon, const int ix_s, const int ix_f, const bool last_inside) { const vec& s = polygon[ix_s]; const vec& f = polygon[ix_f]; bool this_inside = 0.f < clip_boundary(plane_point, plane_normal, f); int length = 0; switch ((last_inside << 1) | (this_inside << 0)) { case 0b00: // no output length = 0; break; case 0b01: // I, F length = 2; { const auto i = line_plane_intersection(plane_point, plane_normal, s, f); *output++ = i; *output++ = f; } break; case 0b10: // I length = 1; { const auto i = line_plane_intersection(plane_point, plane_normal, s, f); *output++ = i; } break; case 0b11: // F length = 1; *output++ = f; break; } bool end_of_polygon = ix_f == (polygon_len - 1); if (!end_of_polygon) { return length + clip_polygon1(output, plane_point, plane_normal, polygon, ix_f, ix_f + 1, this_inside); } else { return length; } } template int clip_polygon(vec * output, const vec& plane_point, const vec& plane_normal, const vec * polygon ) { const vec f = polygon[polygon_len - 1]; // It would be nice to remove the extra dot product, but the // alternative seems likely uglier. bool this_inside = 0.f < clip_boundary(plane_point, plane_normal, f); return clip_polygon1(output, plane_point, plane_normal, polygon, polygon_len - 1, // ix_s 0, // ix_f this_inside); } template inline int clip_polygon1_uv(vec * output, vec * output_uv, const vec plane_point, const vec plane_normal, const vec * polygon, const vec * polygon_uv, const int ix_s, const int ix_f, const bool last_inside) { const vec& s = polygon[ix_s]; const vec& f = polygon[ix_f]; const vec& s_uv = polygon_uv[ix_s]; const vec& f_uv = polygon_uv[ix_f]; bool this_inside = 0.f < clip_boundary(plane_point, plane_normal, f); int length = 0; switch ((last_inside << 1) | (this_inside << 0)) { case 0b00: // no output length = 0; break; case 0b01: // I, F length = 2; { auto [i, i_uv] = line_plane_intersection_two_lines(plane_point, plane_normal, s, f, s_uv, f_uv); *output++ = i; *output_uv++ = i_uv; *output++ = f; *output_uv++ = f_uv; } break; case 0b10: // I length = 1; { auto [i, i_uv] = line_plane_intersection_two_lines(plane_point, plane_normal, s, f, s_uv, f_uv); *output++ = i; *output_uv++ = i_uv; } break; case 0b11: // F length = 1; *output++ = f; *output_uv++ = f_uv; break; } bool end_of_polygon = ix_f == (polygon_len - 1); if (!end_of_polygon) { return length + clip_polygon1_uv(output, output_uv, plane_point, plane_normal, polygon, polygon_uv, ix_f, ix_f + 1, this_inside); } else { return length; } } template int clip_polygon_uv(vec * output, vec * output_uv, const vec& plane_point, const vec& plane_normal, const vec * polygon, const vec * polygon_uv ) { const vec f = polygon[polygon_len - 1]; // It would be nice to remove the extra dot product, but the // alternative seems likely uglier. bool this_inside = 0.f < clip_boundary(plane_point, plane_normal, f); return clip_polygon1_uv(output, output_uv, plane_point, plane_normal, polygon, polygon_uv, polygon_len - 1, // ix_s 0, // ix_f this_inside); } }