Browse Source

Begin creating an implementation of the weiler_atherton polygon clipping algorthm.

Sam Jaffe 6 years ago
parent
commit
ac3b388b33

+ 4 - 0
math/include/game/math/common.hpp

@@ -13,6 +13,10 @@ namespace math {
   vec2 rotate(vec2 const & center, vec2 const & point, radian r);
   dim2::quad rotate(vec2 const & center, dim2::quad const & q, radian r);
 
+  enum orientation { colinear = 0, clockwise = 1, anticlockwise = 2 };
+
+  orientation orient(dim2::line const & ln, dim2::point const & pt);
+
   bool contains(dim2::line const & shape, dim2::point const & pt);
   bool contains(dim2::circle const & shape, dim2::point const & pt);
   bool contains(dim2::quad const & shape, dim2::point const & pt);

+ 4 - 0
math/math.xcodeproj/project.pbxproj

@@ -13,6 +13,7 @@
 		CD1FCFEC227E4C2E00F9BF93 /* common.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CD3C80BA1D68902300ACC795 /* common.cpp */; };
 		CD3AC71E1D2C0AF8002B4BB0 /* shape.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CD3AC71C1D2C0AF8002B4BB0 /* shape.cpp */; };
 		CD3C809F1D675AB100ACC795 /* angle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CD3C809D1D675AB100ACC795 /* angle.cpp */; };
+		CD7E87662294D4BB00D877FE /* weiler_atherton.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CD7E87652294D4BB00D877FE /* weiler_atherton.cxx */; };
 		CDA34D9522517967008036A7 /* matrix_helpers.hpp in Headers */ = {isa = PBXBuildFile; fileRef = CDA34D8C22517680008036A7 /* matrix_helpers.hpp */; };
 		CDA34D9622517969008036A7 /* matrix.hpp in Headers */ = {isa = PBXBuildFile; fileRef = CDA34D8D22517680008036A7 /* matrix.hpp */; };
 		CDA34D972251796B008036A7 /* vector.hpp in Headers */ = {isa = PBXBuildFile; fileRef = CDA34D9022517689008036A7 /* vector.hpp */; };
@@ -73,6 +74,7 @@
 		CD3AC71C1D2C0AF8002B4BB0 /* shape.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = shape.cpp; sourceTree = "<group>"; };
 		CD3C809D1D675AB100ACC795 /* angle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = angle.cpp; sourceTree = "<group>"; };
 		CD3C80BA1D68902300ACC795 /* common.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = common.cpp; sourceTree = "<group>"; };
+		CD7E87652294D4BB00D877FE /* weiler_atherton.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = weiler_atherton.cxx; sourceTree = "<group>"; };
 		CDA34D89225175CB008036A7 /* game */ = {isa = PBXFileReference; lastKnownFileType = folder; name = game; path = include/game; sourceTree = "<group>"; };
 		CDA34D8C22517680008036A7 /* matrix_helpers.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = matrix_helpers.hpp; path = matrix/matrix_helpers.hpp; sourceTree = SOURCE_ROOT; };
 		CDA34D8D22517680008036A7 /* matrix.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = matrix.hpp; path = matrix/matrix.hpp; sourceTree = SOURCE_ROOT; };
@@ -156,6 +158,7 @@
 				CD3AC71C1D2C0AF8002B4BB0 /* shape.cpp */,
 				CD3C809D1D675AB100ACC795 /* angle.cpp */,
 				CD3C80BA1D68902300ACC795 /* common.cpp */,
+				CD7E87652294D4BB00D877FE /* weiler_atherton.cxx */,
 			);
 			path = src;
 			sourceTree = "<group>";
@@ -351,6 +354,7 @@
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				CD7E87662294D4BB00D877FE /* weiler_atherton.cxx in Sources */,
 				CD3AC71E1D2C0AF8002B4BB0 /* shape.cpp in Sources */,
 				CD1FCFEC227E4C2E00F9BF93 /* common.cpp in Sources */,
 				CD3C809F1D675AB100ACC795 /* angle.cpp in Sources */,

+ 0 - 2
math/src/common.cpp

@@ -26,8 +26,6 @@ namespace math {
             rotate(c, q.ul, r)};
   }
 
-  enum orientation { colinear = 0, clockwise = 1, anticlockwise = 2 };
-
   orientation orient(dim2::line const & ln, dim2::point const & pt) {
     auto val = (ln.second[1] - ln.first[1]) * (pt[0] - ln.second[0]) -
                (ln.second[0] - ln.first[0]) * (pt[1] - ln.second[1]);

+ 76 - 0
math/src/weiler_atherton.cxx

@@ -0,0 +1,76 @@
+//
+//  weiler_atherton.cxx
+//  math
+//
+//  Created by Sam Jaffe on 5/21/19.
+//  Copyright © 2019 Sam Jaffe. All rights reserved.
+//
+
+#include <vector>
+
+#include "expect/expect.hpp"
+#include "game/math/common.hpp"
+#include "game/math/shape.hpp"
+
+namespace math {
+  // https://github.com/cabooom/weiler-atherton
+  using polygon = std::vector<dim2::point>;
+
+  struct weiler_atherton {
+    weiler_atherton(polygon const & s, polygon const & c);
+
+    static void inject_point(std::vector<dim2::point> & vec,
+                             dim2::line const & on, dim2::point const & pt);
+    void test_intersection(dim2::line const & s, dim2::line const & c);
+
+    polygon const &subject, clipping;
+    std::vector<dim2::point> p_subject, p_clipping;
+    std::vector<dim2::point> enters, exits;
+  };
+
+  weiler_atherton::weiler_atherton(polygon const & s, polygon const & c)
+      : subject(s), clipping(c), p_subject(s), p_clipping(c) {}
+
+  void weiler_atherton::inject_point(std::vector<dim2::point> & vec,
+                                     dim2::line const & on,
+                                     dim2::point const & pt) {
+    auto start = std::find(vec.begin(), vec.end(), on.first);
+    auto end = std::find(vec.begin(), vec.end(), on.second);
+    auto lnsq = [](dim2::point const & a) { return (a).dot(a); };
+    float dist = lnsq(pt - *start);
+    auto cmp = [&](dim2::point const & a) { return lnsq(*start - a) >= dist; };
+    vec.insert(std::find_if(start, end, cmp), pt);
+  }
+
+  void weiler_atherton::test_intersection(dim2::line const & s,
+                                          dim2::line const & c) {
+    expects_graceful(intersects(s, c), void());
+    auto point = lines::intersection(s, c);
+    if (orient(s, point) == anticlockwise) {
+      enters.emplace_back(point);
+    } else {
+      exits.emplace_back(point);
+    }
+    inject_point(p_subject, s, point);
+    inject_point(p_clipping, c, point);
+  }
+
+  std::vector<polygon> intersection(polygon const & lhs, polygon const & rhs) {
+    std::vector<polygon> rval;
+    expects_graceful(lhs.size() >= 3 && rhs.size() >= 3, rval);
+    // TODO: Assert that lhs and rhs are clockwise polygons with orient
+
+    weiler_atherton wa(lhs, rhs);
+
+    for (std::size_t i = 0; i < lhs.size(); ++i) {
+      std::size_t i2 = (i + 1) % lhs.size();
+      dim2::line left{lhs[i], lhs[i2]};
+      for (std::size_t j = 0; j < rhs.size(); ++j) {
+        std::size_t j2 = (j + 1) % rhs.size();
+        wa.test_intersection(left, {rhs[j], rhs[j2]});
+      }
+    }
+
+    return rval;
+  }
+}