1 | class CVec2D |
2 | { |
3 | public: |
4 | float x, y; |
5 | float magnitude; |
6 | |
7 | CVec2D() {} |
8 | CVec2D(float xPos, float yPos) : x(xPos), y(yPos) {} |
9 | ~CVec2D() {} |
10 | float Magnitude() {return sqrt(x * x + y * y);} |
11 | void Normalize() {x = x / Magnitude(); y = y / Magnitude();} |
12 | float DotProduct(CVec2D vec) {return x * vec.x + y * vec.y;} |
13 | }; |
14 | |
15 | struct CollisionResult |
16 | { |
17 | bool collided, willCollide; |
18 | CVec2D translateVec; |
19 | }; |
20 | |
21 | class CPoly |
22 | { |
23 | private: |
24 | vector<CVec2D> m_points, m_sides; |
25 | float m_radius; |
26 | |
27 | void ProjectPoly(CVec2D axis, CPoly &poly, float &min, float &max) |
28 | { |
29 | // project all the polygon's points onto the axis using the dot product |
30 | float dp = axis.DotProduct(poly.GetPoint(0)); |
31 | min = dp; |
32 | max = dp; |
33 | for(int i = 0; i < poly.PointAmount(); i++) |
34 | { |
35 | dp = poly.GetPoint(i).DotProduct(axis); |
36 | if(dp < min) min = dp; |
37 | else if(dp > max) max = dp; |
38 | } |
39 | } |
40 | float IntervalDist(float min, float max, float min2, float max2) |
41 | { |
42 | if(min < min2) return min2 - max; |
43 | else return min - max2; |
44 | } |
45 | public: |
46 | CPoly() {} |
47 | ~CPoly() {} |
48 | |
49 | void AddPoint(float x, float y) {m_points.push_back(CVec2D(x, y));} |
50 | void Clear() {m_points.clear();} |
51 | int PointAmount() {return m_points.size();} |
52 | int SideAmount() {return m_sides.size();} |
53 | CVec2D GetPoint(int index) {return m_points[index];} |
54 | CVec2D GetSide(int index) {return m_sides[index];} |
55 | void BuildSides() |
56 | { |
57 | CVec2D v1, v2; |
58 | m_sides.clear(); |
59 | for(int i = 0; i < m_points.size(); i++) |
60 | { |
61 | v1 = m_points<i>; |
62 | if(i + 1 < m_points.size()) v2 = m_points[i + 1]; |
63 | else v2 = m_points[0]; |
64 | m_sides.push_back(CVec2D(v2.x - v1.x, v2.y - v1.y)); |
65 | } |
66 | // find the minimum radius that surrounds the polygon |
67 | float currentMax = 574969476.0; |
68 | CVec2D cen = Center(); |
69 | for(int i = 0; i < m_points.size(); i++) |
70 | { |
71 | float side1, side2; |
72 | side1 = fabs(m_points<i>.x - cen.x); |
73 | side2 = fabs(m_points<i>.y - cen.y); |
74 | float dist = sqrt(side1 * side1 + side2 * side2); |
75 | if(dist > currentMax) currentMax = dist; |
76 | } |
77 | m_radius = currentMax; |
78 | } |
79 | void Draw(BITMAP *buf) |
80 | { |
81 | for(int i = 0; i < m_points.size(); i++) |
82 | { |
83 | if(i + 1 < m_points.size()) line(buf, m_points<i>.x, m_points<i>.y, m_points[i + 1].x, m_points[i + 1].y, makecol(255, 255, 255)); |
84 | else line(buf, m_points<i>.x, m_points<i>.y, m_points[0].x, m_points[0].y, makecol(255, 255, 255)); |
85 | } |
86 | } |
87 | void Move(float xAmount, float yAmount) |
88 | { |
89 | for(int i = 0; i < m_points.size(); i++) |
90 | { |
91 | m_points<i>.x += xAmount; |
92 | m_points<i>.y += yAmount; |
93 | } |
94 | } |
95 | CVec2D Center() |
96 | { |
97 | // find the center |
98 | float xTotal = 0, yTotal = 0; |
99 | for(int i = 0; i < m_points.size(); i++) |
100 | { |
101 | xTotal += m_points<i>.x; |
102 | yTotal += m_points<i>.y; |
103 | } |
104 | float xCenter = xTotal / m_points.size(); |
105 | float yCenter = yTotal / m_points.size(); |
106 | return CVec2D(xCenter, yCenter); |
107 | } |
108 | float Radius() {return m_radius;} |
109 | CollisionResult Collision(CPoly &poly, CVec2D velocity) |
110 | { |
111 | CollisionResult result; |
112 | result.collided = true; |
113 | result.willCollide = true; |
114 | result.translateVec = CVec2D(0, 0); |
115 | // do circle collision first to check if they are in the same area |
116 | CVec2D cen1 = Center(), cen2 = poly.Center(); |
117 | if(pow(cen1.x - cen2.x, 2) + pow(cen1.y - cen2.y, 2) > pow(Radius() + poly.Radius(), 2)) |
118 | { |
119 | result.collided = false; |
120 | result.willCollide = false; |
121 | return result; |
122 | } |
123 | float minIntervalDist = 4294967295; |
124 | CVec2D translationAxis(0, 0); |
125 | CVec2D side(0, 0), transAxis(0, 0); |
126 | // loop through all the sides |
127 | for(int i = 0; i < m_sides.size() + poly.PointAmount(); i++) |
128 | { |
129 | if(i < m_sides.size()) side = m_sides<i>; |
130 | else side = poly.GetSide(i - m_sides.size()); |
131 | |
132 | // find the sides normal |
133 | CVec2D axis = CVec2D(-side.y, side.x); |
134 | axis.Normalize(); |
135 | |
136 | float min = 0, max = 0, min2 = 0, max2 = 0; |
137 | ProjectPoly(axis, *this, min, max); |
138 | ProjectPoly(axis, poly, min2, max2); |
139 | |
140 | if(IntervalDist(min, max, min2, max2) > 0) result.collided = false; |
141 | |
142 | // check if the polygons will collide |
143 | float velocityProj = axis.DotProduct(velocity); |
144 | |
145 | if(velocityProj < 0) min += velocityProj; |
146 | else max += velocityProj; |
147 | |
148 | float intervalDist = IntervalDist(min, max, min2, max2); |
149 | if(intervalDist > 0) result.willCollide = false; |
150 | |
151 | // nothing collides |
152 | if(!result.collided && !result.willCollide) break; |
153 | |
154 | intervalDist = abs(intervalDist); |
155 | if(intervalDist < minIntervalDist) |
156 | { |
157 | minIntervalDist = intervalDist; |
158 | translationAxis = axis; |
159 | CVec2D vec(Center().x - poly.Center().x, Center().y - poly.Center().y); |
160 | if(vec.DotProduct(translationAxis) < 0) |
161 | { |
162 | translationAxis.x = -translationAxis.x; |
163 | translationAxis.y = -translationAxis.y; |
164 | } |
165 | } |
166 | } |
167 | if(result.willCollide) |
168 | { |
169 | // calc the translate vector for the polygon |
170 | CVec2D translateVec(translationAxis.x * minIntervalDist, translationAxis.y * minIntervalDist); |
171 | result.translateVec = translateVec; |
172 | } |
173 | return result; |
174 | } |
175 | }; |