10 QRectF addBBox(QRectF r1, QRectF r2)
12 // Find smallest QRectF containing given rectangles
16 if (r1.left() <= r2.left() )
17 n.setLeft(r1.left() );
19 n.setLeft(r2.left() );
22 if (r1.top() <= r2.top() )
28 if (r1.right() <= r2.right() )
29 n.setRight(r2.right() );
31 n.setRight(r1.right() );
34 if (r1.bottom() <= r2.bottom() )
35 n.setBottom(r2.bottom() );
37 n.setBottom(r1.bottom() );
41 bool isInBox(const QPointF &p, const QRectF &box)
43 if (p.x() >= box.left() && p.x() <= box.right()
44 && p.y() <= box.bottom() && p.y() >= box.top() )
49 ConvexPolygon::ConvexPolygon ()
53 ConvexPolygon::ConvexPolygon (QPolygonF p):QPolygonF (p)
57 void ConvexPolygon::calcCentroid()
59 // Calculate area and centroid
60 // http://en.wikipedia.org/wiki/Centroid
66 for (int i=0;i<size()-1;i++)
68 p=at(i).x() * at(i+1).y() - at(i+1).x() * at(i).y();
70 cx+=(at(i).x()+at(i+1).x()) * p;
71 cy+=(at(i).y()+at(i+1).y()) * p;
74 // area is negative if vertices ordered counterclockwise
75 // (in mirrored graphicsview!)
78 _centroid.setX (cx/p);
79 _centroid.setY (cy/p);
82 QPointF ConvexPolygon::centroid() const
87 qreal ConvexPolygon::weight() const
93 QPointF normalize (const QPointF &p)
95 if (p==QPointF(0,0)) return p;
96 qreal l=sqrt ( p.x()*p.x() + p.y()*p.y() );
97 return QPointF (p.x()/l,p.y()/l);
100 //! Dot product of two vectors
101 qreal dotProduct (const QPointF &a, const QPointF &b)
103 return a.x()*b.x() + a.y()*b.y();
107 QPointF scale (const QPointF &v,const qreal &f)
109 return QPointF (v.x()*f,v.y()*f);
112 QPointF invert (const QPointF &v)
114 return QPointF (-v.x(),-v.y());
117 /*! Calculate the projection of a polygon on an axis
118 and returns it as a [min, max] interval */
120 void projectPolygon(QPointF axis, QPolygonF polygon, qreal &min, qreal &max)
122 // To project a point on an axis use the dot product
124 //cout << "Projecting on "<< axis<<endl;
125 qreal d = dotProduct(axis,polygon.at(0));
128 for (int i = 0; i < polygon.size(); i++)
130 d= dotProduct (polygon.at(i),axis);
135 // cout << "p="<<polygon.at(i)<<" d="<<d<<" (min, max)=("<<min<<","<<max<<")\n";
139 // Calculate the signed distance between [minA, maxA] and [minB, maxB]
140 // The distance will be negative if the intervals overlap
142 qreal intervalDistance(qreal minA, qreal maxA, qreal minB, qreal maxB) {
151 Check if polygon A is going to collide with polygon B.
152 The last parameter is the *relative* velocity
153 of the polygons (i.e. velocityA - velocityB)
156 PolygonCollisionResult polygonCollision(QPolygonF polygonA,
157 QPolygonF polygonB, QPointF velocity)
159 PolygonCollisionResult result;
160 result.intersect = true;
161 result.willIntersect = true;
163 int edgeCountA = polygonA.size();
164 int edgeCountB = polygonB.size();
165 qreal minIntervalDistance = 1000000000;
166 QPointF translationAxis;
171 for (int k=0; k<edgeCountA;k++)
172 cout <<polygonA.at(k);
174 for (int k=0; k<edgeCountB;k++)
175 cout <<polygonB.at(k);
179 // Loop through all the edges of both polygons
180 for (int i=0;i<edgeCountA + edgeCountB;i++)
184 // Loop through polygon A
187 polygonA.at(i+1).x()-polygonA.at(i).x(),
188 polygonA.at(i+1).y()-polygonA.at(i).y());
191 polygonA.at(0).x()-polygonA.at(i).x(),
192 polygonA.at(0).y()-polygonA.at(i).y());
195 // Loop through polygon B
196 if (i < edgeCountA +edgeCountB -1 )
198 polygonB.at(i-edgeCountA+1).x() - polygonB.at(i-edgeCountA).x(),
199 polygonB.at(i-edgeCountA+1).y() - polygonB.at(i-edgeCountA).y());
202 polygonB.at(0).x() - polygonB.at(i-edgeCountA).x(),
203 polygonB.at(0).y() - polygonB.at(i-edgeCountA).y());
206 // ===== 1. Find if the polygons are currently intersecting =====
208 // Find the axis perpendicular to the current edge
210 QPointF axis (-edge.y(), edge.x());
211 axis=normalize(axis);
213 // Find the projection of the polygon on the current axis
215 qreal minA = 0; qreal minB = 0; qreal maxA = 0; qreal maxB = 0;
216 projectPolygon(axis, polygonA, minA, maxA);
217 projectPolygon(axis, polygonB, minB, maxB);
219 // Check if the polygon projections are currentlty intersecting
221 qreal d = intervalDistance(minA, maxA, minB, maxB);
222 if (d > 0) result.intersect = false;
224 // ===== 2. Now find if the polygons *will* intersect =====
227 // Project the velocity on the current axis
229 qreal velocityProjection = dotProduct(axis,velocity);
231 // Get the projection of polygon A during the movement
233 if (velocityProjection < 0)
234 minA += velocityProjection;
236 maxA += velocityProjection;
238 // Do the same test as above for the new projection
240 // d = intervalDistance(minA, maxA, minB, maxB);
241 //if (d > 0) result.willIntersect = false;
244 cout << "edge="<<edge<<" ";
245 cout <<"axis="<<axis<<" ";
246 cout <<"dA=("<<minA<<","<<maxA<<") dB=("<<minB<<","<<maxB<<")";
247 cout <<" d="<<d<<" ";
248 //cout <<"minD="<<minIntervalDistance<<" ";
249 cout <<"int="<<result.intersect<<" ";
250 //cout <<"wint="<<result.willIntersect<<" ";
251 //cout <<"velProj="<<velocityProjection<<" ";
255 if (result.intersect )// || result.willIntersect)
257 // Check if the current interval distance is the minimum one. If so
258 // store the interval distance and the current distance. This will
259 // be used to calculate the minimum translation vector
262 if (d < minIntervalDistance) {
263 minIntervalDistance = d;
264 //translationAxis = axis;
265 //cout << "tAxix="<<translationAxis<<endl;
267 //QPointF t = polygonA.Center - polygonB.Center;
268 //QPointF t = polygonA.at(0) - polygonB.at(0);
269 //if (dotProduct(t,translationAxis) < 0)
270 // translationAxis = -translationAxis;
275 // The minimum translation vector
276 // can be used to push the polygons appart.
278 if (result.willIntersect)
279 result.minTranslation =
280 translationAxis * minIntervalDistance;
285 /* The function can be used this way:
286 QPointF polygonATranslation = new QPointF();
291 PolygonCollisionResult r = PolygonCollision(polygonA, polygonB, velocity);
294 // Move the polygon by its velocity, then move
295 // the polygons appart using the Minimum Translation Vector
296 polygonATranslation = velocity + r.minTranslation;
298 // Just move the polygon by its velocity
299 polygonATranslation = velocity;
301 polygonA.Offset(polygonATranslation);