geometry.cpp
author insilmaril
Mon, 24 Aug 2009 14:39:07 +0000
changeset 789 d85834ad8c54
parent 754 db0ec4bcf416
child 792 7d67be709091
permissions -rw-r--r--
Fixed collision methods, segfault for wrong flagname
     1 #include "geometry.h"
     2 
     3 #include <math.h>
     4 #include "misc.h"
     5 
     6 #include <iostream>
     7 using namespace std;
     8 
     9 
    10 QRectF addBBox(QRectF r1, QRectF r2)
    11 {	
    12 	// Find smallest QRectF containing given rectangles
    13 
    14 	QRectF n;
    15 	// Set left border
    16 	if (r1.left() <= r2.left() )
    17 		n.setLeft(r1.left() );
    18 	else
    19 		n.setLeft(r2.left() );
    20 		
    21 	// Set top border		
    22 	if (r1.top() <= r2.top() )
    23 		n.setTop(r1.top() );
    24 	else
    25 		n.setTop(r2.top() );
    26 		
    27 	// Set right border
    28 	if (r1.right() <= r2.right() )
    29 		n.setRight(r2.right() );
    30 	else
    31 		n.setRight(r1.right() );
    32 		
    33 	// Set bottom 
    34 	if (r1.bottom() <= r2.bottom() )
    35 		n.setBottom(r2.bottom() );
    36 	else
    37 		n.setBottom(r1.bottom() );
    38 	return n;
    39 }
    40 
    41 bool isInBox(const QPointF &p, const QRectF &box)
    42 {
    43     if (p.x() >= box.left() && p.x() <= box.right()  
    44 	&& p.y() <= box.bottom() && p.y() >= box.top() )
    45 		return true;
    46     return false;	
    47 }
    48 
    49 ConvexPolygon::ConvexPolygon ()
    50 {
    51 }
    52 
    53 ConvexPolygon::ConvexPolygon (QPolygonF p):QPolygonF (p)
    54 {
    55 }
    56 
    57 void ConvexPolygon::calcCentroid() 
    58 {
    59 	// Calculate area and centroid
    60 	// http://en.wikipedia.org/wiki/Centroid
    61 	qreal cx,cy,p;
    62 	cx=cy=0;
    63 	_area=0;
    64 
    65 	append (at(0));
    66 	for (int i=0;i<size()-1;i++)
    67 	{
    68 		p=at(i).x() * at(i+1).y() - at(i+1).x() * at(i).y();
    69 		_area+=p;
    70 		cx+=(at(i).x()+at(i+1).x()) * p;
    71 		cy+=(at(i).y()+at(i+1).y()) * p;
    72 	}	
    73 	pop_back();
    74 	// area is negative if vertices ordered counterclockwise
    75 	// (in mirrored graphicsview!)
    76 	_area=_area/2;	
    77 	p=_area*6;
    78 	_centroid.setX (cx/p);
    79 	_centroid.setY (cy/p);
    80 }
    81 
    82 QPointF ConvexPolygon::centroid() const
    83 {
    84 	return _centroid;
    85 }
    86 
    87 qreal ConvexPolygon::weight() const
    88 {
    89 	return _area;
    90 }
    91 
    92 //! Normalize vector
    93 QPointF normalize (const QPointF &p)
    94 {
    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);
    98 }
    99 
   100 //! Dot product of two vectors
   101 qreal dotProduct (const QPointF &a, const QPointF &b)
   102 {
   103 	return a.x()*b.x() + a.y()*b.y();
   104 }
   105 
   106 
   107 QPointF scale (const QPointF &v,const qreal &f)
   108 {
   109 	return QPointF (v.x()*f,v.y()*f);
   110 }
   111 
   112 QPointF invert (const QPointF &v)
   113 {
   114 	return QPointF (-v.x(),-v.y());
   115 }
   116 
   117 /*! Calculate the projection of a polygon on an axis
   118     and returns it as a [min, max] interval  */
   119 
   120 void projectPolygon(QPointF axis, QPolygonF polygon, qreal &min, qreal &max) 
   121 {
   122     // To project a point on an axis use the dot product
   123 
   124 	//cout << "Projecting on "<< axis<<endl;
   125     qreal d = dotProduct(axis,polygon.at(0));
   126     min = d;
   127     max = d;
   128     for (int i = 0; i < polygon.size(); i++) 
   129 	{
   130         d= dotProduct (polygon.at(i),axis);
   131         if (d < min) 
   132             min = d;
   133         else 
   134             if (d> max) max = d;
   135 	//	cout << "p="<<polygon.at(i)<<"  d="<<d<<"  (min, max)=("<<min<<","<<max<<")\n";	
   136     }
   137 }
   138 
   139 // Calculate the signed distance between [minA, maxA] and [minB, maxB]
   140 // The distance will be negative if the intervals overlap
   141 
   142 qreal intervalDistance(qreal minA, qreal maxA, qreal minB, qreal maxB) {
   143     if (minA < minB) {
   144         return minB - maxA;
   145     } else {
   146         return minA - maxB;
   147     }
   148 }
   149 
   150 /*!
   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)
   154 */
   155 
   156 PolygonCollisionResult polygonCollision(QPolygonF polygonA, 
   157                               QPolygonF polygonB, QPointF velocity) 
   158 {
   159     PolygonCollisionResult result;
   160     result.intersect = true;
   161     result.willIntersect = true;
   162 
   163     int edgeCountA = polygonA.size();
   164     int edgeCountB = polygonB.size();
   165     qreal minIntervalDistance = 1000000000;
   166     QPointF translationAxis;
   167     QPointF edge;
   168 
   169 /*
   170 	cout << "\nA: ";
   171 	for (int k=0; k<edgeCountA;k++)
   172 		cout <<polygonA.at(k);
   173 	cout << "\nB: ";
   174 	for (int k=0; k<edgeCountB;k++)
   175 		cout <<polygonB.at(k);
   176 	cout <<endl;	
   177 */		
   178 		
   179     // Loop through all the edges of both polygons
   180 	for (int i=0;i<edgeCountA + edgeCountB;i++)
   181 	{
   182         if (i< edgeCountA) 
   183 		{
   184 			// Loop through polygon A
   185 			if (i<edgeCountA-1)
   186 				edge = QPointF (
   187 					polygonA.at(i+1).x()-polygonA.at(i).x(), 
   188 					polygonA.at(i+1).y()-polygonA.at(i).y());
   189 			else		
   190 				edge = QPointF (
   191 					polygonA.at(0).x()-polygonA.at(i).x(), 
   192 					polygonA.at(0).y()-polygonA.at(i).y());
   193         } else 
   194 		{
   195 			// Loop through polygon B
   196 			if (i < edgeCountA +edgeCountB -1 )
   197 				edge = QPointF (
   198 					polygonB.at(i-edgeCountA+1).x() - polygonB.at(i-edgeCountA).x(), 
   199 					polygonB.at(i-edgeCountA+1).y() - polygonB.at(i-edgeCountA).y());
   200 			else	
   201 				edge = QPointF (
   202 					polygonB.at(0).x() - polygonB.at(i-edgeCountA).x(), 
   203 					polygonB.at(0).y() - polygonB.at(i-edgeCountA).y());
   204 		}
   205 
   206         // ===== 1. Find if the polygons are currently intersecting =====
   207 
   208         // Find the axis perpendicular to the current edge
   209 
   210         QPointF axis (-edge.y(), edge.x());
   211         axis=normalize(axis);
   212 
   213         // Find the projection of the polygon on the current axis
   214 
   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);
   218 
   219         // Check if the polygon projections are currentlty intersecting
   220 
   221         qreal d = intervalDistance(minA, maxA, minB, maxB);
   222         if (d > 0) result.intersect = false;
   223 
   224        // ===== 2. Now find if the polygons *will* intersect =====
   225 
   226 
   227         // Project the velocity on the current axis
   228 
   229         qreal velocityProjection = dotProduct(axis,velocity);
   230 
   231         // Get the projection of polygon A during the movement
   232 
   233         if (velocityProjection < 0) 
   234             minA += velocityProjection;
   235         else 
   236             maxA += velocityProjection;
   237 
   238         // Do the same test as above for the new projection
   239 
   240         // d = intervalDistance(minA, maxA, minB, maxB);
   241         //if (d > 0) result.willIntersect = false;
   242 		/*
   243 		cout <<"   ";
   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<<"  ";
   252 		cout <<endl;
   253 		*/
   254 	
   255         if (result.intersect )// || result.willIntersect) 
   256 		{
   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
   260 
   261 			if (d<0) d=-d;
   262 			if (d < minIntervalDistance) {
   263 				minIntervalDistance = d;
   264 				//translationAxis = axis;
   265 				//cout << "tAxix="<<translationAxis<<endl;
   266 
   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;
   271 			}
   272 		}
   273     }
   274 
   275     // The minimum translation vector
   276     // can be used to push the polygons appart.
   277 
   278     if (result.willIntersect)
   279         result.minTranslation = 
   280                translationAxis * minIntervalDistance;
   281     
   282     return result;
   283 }
   284 
   285 /* The function can be used this way: 
   286    QPointF polygonATranslation = new QPointF();
   287 */   
   288 
   289 
   290 /*
   291 PolygonCollisionResult r = PolygonCollision(polygonA, polygonB, velocity);
   292 
   293 if (r.WillIntersect) 
   294   // Move the polygon by its velocity, then move
   295   // the polygons appart using the Minimum Translation Vector
   296   polygonATranslation = velocity + r.minTranslation;
   297 else 
   298   // Just move the polygon by its velocity
   299   polygonATranslation = velocity;
   300 
   301 polygonA.Offset(polygonATranslation);
   302 
   303 */
   304 
   305