diff --git a/src/engine/collision.cpp b/src/engine/collision.cpp index 724a775..fbf94c3 100644 --- a/src/engine/collision.cpp +++ b/src/engine/collision.cpp @@ -109,47 +109,63 @@ namespace egn{ //If not in the rectangle, we can have 1 or 2 candidate edges to project onto. We want the closest one //to the input point. math::vec3 cand1, cand2; - if(r1 > r2){ - cand1 = lineseg_point_proj(line_segment{l.point1, l.point4}, r); - if(r4 > r5){ - //candidates be and bc - cand2 = lineseg_point_proj(line_segment{l.point1, l.point2}, r); - }else if(r6 < r5){ - //candidates be and de - cand2 = lineseg_point_proj(line_segment{l.point3, l.point4}, r); - }else{ - //candidate be - return cand1; - } - }else if(r3 < r2){ - cand1 = lineseg_point_proj(line_segment{l.point2, l.point3}, r); - if(r4 > r5){ - //candidates cd and bc - cand2 = lineseg_point_proj(line_segment{l.point1, l.point2}, r); - }else if(r6 < r5){ - //candidates cd and de - cand2 = lineseg_point_proj(line_segment{l.point3, l.point4}, r); - }else{ - //candidate cd - return cand1; - } - }else if(r4 > r5){ - //candidate bc - cand1 = lineseg_point_proj(line_segment{l.point1, l.point2}, r); - return cand1; - }else if(r6 < r5){ - //candidate de - cand1 = lineseg_point_proj(line_segment{l.point3, l.point4}, r); - return cand1; - }else{ - //point is inside rectangle - return r; - } - //If we make it here, there are 2 candidate edges and we pick the closer one - math::vec3 diff1 = r - cand1; - math::vec3 diff2 = r - cand2; - return (diff1 * diff1) > (diff2 * diff2) ? cand1 : cand2; + /* + now we divide the plane into a voronoi diagram where each region is closest + to either a line or a point on the rectangle. We use the r values calculated above + to determine which region the point lies within. + + a b c + . . + 1 a1 . b1 . c1 + . . + - - - - -*---------*- - - - - + | | + 2 a2 | b2 | c2 + | | + - - - - -*---------*- - - - - + . . + 3 a3 . b3 . c3 + . . + */ + + if(r5 < r4){ + //in column a + if(r2 < r1){ + //a3 + return l.point1; + }else if(r2 > r3){ + //a1 + return l.point2; + }else{ + //a2 + return lineseg_point_proj(line_segment{l.point1, l.point2}, r); + } + }else if (r5 > r6){ + //in column c + if(r2 < r1){ + //c3 + return l.point4; + }else if(r2 > r3){ + //c1 + return l.point3; + }else{ + //c2 + return lineseg_point_proj(line_segment{l.point3, l.point4}, r); + } + }else{ + //in column b + if(r2 < r1){ + //b3 + return lineseg_point_proj(line_segment{l.point1, l.point4}, r); + }else if(r2 > r3){ + //b1 + return lineseg_point_proj(line_segment{l.point2, l.point3}, r); + }else{ + //b2 + return r; + } + } } /* bool check_collision(const collidable_iface& l, const collidable_iface& r, float epsilon){ return l.check_collision(r, epsilon); @@ -259,7 +275,8 @@ namespace egn{ //find the nearest point on the rectangle to the newly created coplanar point and see if it's within //epsilon of the original point - return math::magnitude(r - rectangle_point_coplanar_proj(l, projected_point)) <= epsilon; + math::vec3 proj_dist = r - rectangle_point_coplanar_proj(l, projected_point); + return (proj_dist * proj_dist) <= (epsilon * epsilon); //avoid square root } bool check_collision(const point& l, const point& r, float epsilon){ return math::fuzzy_eq(l, r, epsilon); diff --git a/src/tests/collision/rectangle.cpp b/src/tests/collision/rectangle.cpp new file mode 100644 index 0000000..c002767 --- /dev/null +++ b/src/tests/collision/rectangle.cpp @@ -0,0 +1,66 @@ +/** + This file is a part of our_dick + Copyright (C) 2021 rexy712 + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + +#include "engine/collision.hpp" + +bool run_test(const egn::rectangle& l, const egn::point& r, bool desired_result){ + if(egn::check_collision(l, r) != desired_result){ + printf("FAIL\n"); + return false; + }else{ + printf("Succ\n"); + return true; + } +} + +struct testval{ + egn::point p; + bool desres; +}; + +int main(){ + egn::rectangle rect; + rect.point1 = {-1, -2, 1}; + rect.point2 = {-1, 3, 1}; + rect.point3 = {2, 3, 1}; + rect.point4 = {2, -2, 1}; + + egn::point p; + + testval tests[] = { + {{0,0, 1}, true}, + {{1,1, 1}, true}, + {{-1,3, 1}, true}, + {{2,-2, 1}, true}, + {{0,0, 0}, false}, + {{0,0, -1}, false}, + {{-1.1,0, 1}, false}, + {{2.1,0, 1}, false}, + {{0,-2.1, 1}, false}, + {{0,3.1, 1}, false}, + {{-1.1,3.1, 1}, false}, + {{2.1,3.1, 1}, false}, + {{2.1,-2.1, 1}, false}, + }; + + for(const auto& [point,desres] : tests){ + if(bool rv;rv = !run_test(rect, point, desres)) + return rv; + } + return 0; +}