From 0b41a5138da4da3ad9cbd49f7a5dc902c121f400 Mon Sep 17 00:00:00 2001 From: Zack Buhman Date: Sat, 26 Apr 2025 04:30:00 -0500 Subject: [PATCH] math/geometry: add support for 3-coordinate-space clipping --- math/geometry.hpp | 292 +++++++++++++++++++++++++++++++--------------- 1 file changed, 197 insertions(+), 95 deletions(-) diff --git a/math/geometry.hpp b/math/geometry.hpp index 36d50e3..7ac55f3 100644 --- a/math/geometry.hpp +++ b/math/geometry.hpp @@ -4,7 +4,7 @@ namespace geometry { -template +template constexpr inline T line_plane_intersection_d(const vec& plane_point, // p0 const vec& plane_normal, // n @@ -19,7 +19,7 @@ T line_plane_intersection_d(const vec& plane_point, // p0 return intersection; } -template +template constexpr inline vec line_plane_intersection(const vec& plane_point, // p0 const vec& plane_normal, // n @@ -34,32 +34,19 @@ vec line_plane_intersection(const vec& plane_point, // p0 return line_start + line_vector * intersection; } -template +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 - ) +vec interpolate(const vec& line_start, + const vec& line_end, + T intersection + ) { - /* 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 - }; + return line_start + line_vector * intersection; } -template +template T clip_boundary(const vec& plane_point, // X const vec& plane_normal, // Nc const vec& line_point @@ -74,7 +61,7 @@ inline T positive_modulo(T a, T b) return (a % b + b) % b; } -template +template inline int clip_polygon1(vec * output, const vec plane_point, const vec plane_normal, @@ -86,7 +73,7 @@ inline int clip_polygon1(vec * output, const vec& s = polygon[ix_s]; const vec& f = polygon[ix_f]; - bool this_inside = 0.f < clip_boundary(plane_point, + bool this_inside = 0.f < clip_boundary(plane_point, plane_normal, f); @@ -98,7 +85,7 @@ inline int clip_polygon1(vec * output, case 0b01: // I, F length = 2; { - const auto i = line_plane_intersection(plane_point, plane_normal, s, f); + const auto i = line_plane_intersection(plane_point, plane_normal, s, f); *output++ = i; *output++ = f; } @@ -106,7 +93,7 @@ inline int clip_polygon1(vec * output, case 0b10: // I length = 1; { - const auto i = line_plane_intersection(plane_point, plane_normal, s, f); + const auto i = line_plane_intersection(plane_point, plane_normal, s, f); *output++ = i; } break; @@ -119,7 +106,7 @@ inline int clip_polygon1(vec * output, bool end_of_polygon = ix_f == (polygon_len - 1); if (!end_of_polygon) { return length + - clip_polygon1(output, + clip_polygon1(output, plane_point, plane_normal, polygon, @@ -142,11 +129,11 @@ int clip_polygon(vec * output, // 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, + bool this_inside = 0.f < clip_boundary(plane_point, plane_normal, f); - return clip_polygon1(output, + return clip_polygon1(output, plane_point, plane_normal, polygon, @@ -155,104 +142,219 @@ int clip_polygon(vec * output, 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) +template +inline int clip_polygon1_2(vec * output_1, + vec * output_2, + const vec plane_point, + const vec plane_normal, + const vec * polygon_1, + const vec * polygon_2, + 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_1 = polygon_1[ix_s]; + const vec& f_1 = polygon_1[ix_f]; - const vec& s_uv = polygon_uv[ix_s]; - const vec& f_uv = polygon_uv[ix_f]; + const vec& s_2 = polygon_2[ix_s]; + const vec& f_2 = polygon_2[ix_f]; - bool this_inside = 0.f < clip_boundary(plane_point, + bool this_inside = 0.f < clip_boundary(plane_point, plane_normal, - f); + f_1); int length = 0; - switch ((last_inside << 1) | (this_inside << 0)) { + int control = (last_inside << 1) | (this_inside << 0); + switch (control) { 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; + [[fallthrough]]; + case 0b01: // I, F { - 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; + const auto& i_1_start = s_1; + const auto i_1_vector = f_1 - s_1; // l + const T intersection = line_plane_intersection_d(plane_point, plane_normal, + i_1_start, i_1_vector); + const vec i_1 = i_1_start + i_1_vector * intersection; + + *output_1++ = i_1; + *output_2++ = interpolate(s_2, f_2, intersection); + + if (control == 0b01) { // I, F + *output_1++ = f_1; + *output_2++ = f_2; + length = 2; + } else { + length = 1; + } } break; case 0b11: // F + *output_1++ = f_1; + *output_2++ = f_2; 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); + clip_polygon1_2(output_1, + output_2, + plane_point, + plane_normal, + polygon_1, + polygon_2, + 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 - ) +template +int clip_polygon_2(vec * output_1, + vec * output_2, + const vec& plane_point, + const vec& plane_normal, + const vec * polygon_1, + const vec * polygon_2 + ) { - const vec f = polygon[polygon_len - 1]; + const vec f = polygon_1[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, + 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); + return clip_polygon1_2(output_1, + output_2, + plane_point, + plane_normal, + polygon_1, + polygon_2, + polygon_len - 1, // ix_s + 0, // ix_f + this_inside); +} + +template +inline int clip_polygon1_3(vec * output_1, + vec * output_2, + vec * output_3, + const vec plane_point, + const vec plane_normal, + const vec * polygon_1, + const vec * polygon_2, + const vec * polygon_3, + const int ix_s, + const int ix_f, + const bool last_inside) +{ + const vec& s_1 = polygon_1[ix_s]; + const vec& f_1 = polygon_1[ix_f]; + + const vec& s_2 = polygon_2[ix_s]; + const vec& f_2 = polygon_2[ix_f]; + + const vec& s_3 = polygon_3[ix_s]; + const vec& f_3 = polygon_3[ix_f]; + + bool this_inside = 0.f < clip_boundary(plane_point, + plane_normal, + f_1); + + int length = 0; + int control = (last_inside << 1) | (this_inside << 0); + switch (control) { + case 0b00: // no output + length = 0; + break; + case 0b10: // I + [[fallthrough]]; + case 0b01: // I, F + { + const auto& i_1_start = s_1; + const auto i_1_vector = f_1 - s_1; // l + const T intersection = line_plane_intersection_d(plane_point, plane_normal, + i_1_start, i_1_vector); + const vec i_1 = i_1_start + i_1_vector * intersection; + + *output_1++ = i_1; + *output_2++ = interpolate(s_2, f_2, intersection); + *output_3++ = interpolate(s_3, f_3, intersection); + + if (control == 0b01) { // I, F + *output_1++ = f_1; + *output_2++ = f_2; + *output_3++ = f_3; + length = 2; + } else { + length = 1; + } + } + break; + case 0b11: // F + *output_1++ = f_1; + *output_2++ = f_2; + *output_3++ = f_3; + length = 1; + break; + } + + bool end_of_polygon = ix_f == (polygon_len - 1); + if (!end_of_polygon) { + return length + + clip_polygon1_3(output_1, + output_2, + output_3, + plane_point, + plane_normal, + polygon_1, + polygon_2, + polygon_3, + ix_f, + ix_f + 1, + this_inside); + } else { + return length; + } +} + +template +int clip_polygon_3(vec * output_1, + vec * output_2, + vec * output_3, + const vec& plane_point, + const vec& plane_normal, + const vec * polygon_1, + const vec * polygon_2, + const vec * polygon_3 + ) +{ + const vec f = polygon_1[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_3(output_1, + output_2, + output_3, + plane_point, + plane_normal, + polygon_1, + polygon_2, + polygon_3, + polygon_len - 1, // ix_s + 0, // ix_f + this_inside); } }