geometry.cpp
author insilmaril
Tue, 09 Mar 2010 08:29:09 +0000
changeset 831 25a950c2eb98
parent 824 36eb4b8f409e
child 835 31841b366d5e
permissions -rw-r--r--
Introduce loadNote and saveNote commands
insilmaril@650
     1
#include "geometry.h"
insilmaril@650
     2
insilmaril@656
     3
#include <math.h>
insilmaril@662
     4
#include "misc.h"
insilmaril@656
     5
insilmaril@792
     6
#include <QString>
insilmaril@792
     7
insilmaril@789
     8
#include <iostream>
insilmaril@662
     9
using namespace std;
insilmaril@650
    10
insilmaril@789
    11
insilmaril@650
    12
QRectF addBBox(QRectF r1, QRectF r2)
insilmaril@650
    13
{	
insilmaril@650
    14
	// Find smallest QRectF containing given rectangles
insilmaril@650
    15
insilmaril@650
    16
	QRectF n;
insilmaril@650
    17
	// Set left border
insilmaril@650
    18
	if (r1.left() <= r2.left() )
insilmaril@650
    19
		n.setLeft(r1.left() );
insilmaril@650
    20
	else
insilmaril@650
    21
		n.setLeft(r2.left() );
insilmaril@650
    22
		
insilmaril@650
    23
	// Set top border		
insilmaril@650
    24
	if (r1.top() <= r2.top() )
insilmaril@650
    25
		n.setTop(r1.top() );
insilmaril@650
    26
	else
insilmaril@650
    27
		n.setTop(r2.top() );
insilmaril@650
    28
		
insilmaril@650
    29
	// Set right border
insilmaril@650
    30
	if (r1.right() <= r2.right() )
insilmaril@650
    31
		n.setRight(r2.right() );
insilmaril@650
    32
	else
insilmaril@650
    33
		n.setRight(r1.right() );
insilmaril@650
    34
		
insilmaril@650
    35
	// Set bottom 
insilmaril@650
    36
	if (r1.bottom() <= r2.bottom() )
insilmaril@650
    37
		n.setBottom(r2.bottom() );
insilmaril@650
    38
	else
insilmaril@650
    39
		n.setBottom(r1.bottom() );
insilmaril@650
    40
	return n;
insilmaril@650
    41
}
insilmaril@650
    42
insilmaril@754
    43
bool isInBox(const QPointF &p, const QRectF &box)
insilmaril@650
    44
{
insilmaril@650
    45
    if (p.x() >= box.left() && p.x() <= box.right()  
insilmaril@650
    46
	&& p.y() <= box.bottom() && p.y() >= box.top() )
insilmaril@650
    47
		return true;
insilmaril@650
    48
    return false;	
insilmaril@650
    49
}
insilmaril@656
    50
insilmaril@824
    51
qreal distance (const QPointF &p, const QPointF &q)
insilmaril@824
    52
{
insilmaril@824
    53
	return sqrt (p.x()*q.x() + p.y()*q.y());
insilmaril@824
    54
}
insilmaril@824
    55
insilmaril@792
    56
Vector::Vector ():QPointF ()
insilmaril@792
    57
{
insilmaril@792
    58
}
insilmaril@792
    59
insilmaril@792
    60
Vector::Vector (const QPointF &p):QPointF (p)
insilmaril@792
    61
{
insilmaril@792
    62
}
insilmaril@792
    63
insilmaril@792
    64
Vector::Vector (qreal x, qreal y):QPointF (x,y)
insilmaril@792
    65
{
insilmaril@792
    66
}
insilmaril@792
    67
insilmaril@792
    68
//! Normalize vector
insilmaril@792
    69
void Vector::normalize ()
insilmaril@792
    70
{
insilmaril@792
    71
	if (x()==0 && y()==0) return;
insilmaril@792
    72
	qreal l=sqrt ( x()*x() + y()*y() );
insilmaril@792
    73
	setX (x()/l);
insilmaril@792
    74
	setY (y()/l);
insilmaril@792
    75
}
insilmaril@792
    76
insilmaril@792
    77
//! Dot product of two vectors
insilmaril@792
    78
qreal Vector::dotProduct (const QPointF &b)
insilmaril@792
    79
{
insilmaril@792
    80
	return x()*b.x() + y()*b.y();
insilmaril@792
    81
}
insilmaril@792
    82
insilmaril@792
    83
insilmaril@792
    84
void Vector::scale (const qreal &f)
insilmaril@792
    85
{
insilmaril@792
    86
	setX (x()*f);
insilmaril@792
    87
	setY (y()*f);
insilmaril@792
    88
}
insilmaril@792
    89
insilmaril@792
    90
void Vector::invert ()
insilmaril@792
    91
{
insilmaril@792
    92
	setX (-x());
insilmaril@792
    93
	setY (-y());
insilmaril@792
    94
}
insilmaril@792
    95
insilmaril@798
    96
QPointF Vector::toQPointF ()
insilmaril@798
    97
{
insilmaril@798
    98
	return QPointF (x(),y());
insilmaril@798
    99
}
insilmaril@798
   100
insilmaril@792
   101
/*! Calculate the projection of a polygon on an axis
insilmaril@792
   102
    and returns it as a [min, max] interval  */
insilmaril@789
   103
ConvexPolygon::ConvexPolygon ()
insilmaril@789
   104
{
insilmaril@789
   105
}
insilmaril@789
   106
insilmaril@789
   107
ConvexPolygon::ConvexPolygon (QPolygonF p):QPolygonF (p)
insilmaril@789
   108
{
insilmaril@789
   109
}
insilmaril@789
   110
insilmaril@789
   111
void ConvexPolygon::calcCentroid() 
insilmaril@789
   112
{
insilmaril@789
   113
	// Calculate area and centroid
insilmaril@789
   114
	// http://en.wikipedia.org/wiki/Centroid
insilmaril@789
   115
	qreal cx,cy,p;
insilmaril@789
   116
	cx=cy=0;
insilmaril@789
   117
	_area=0;
insilmaril@789
   118
insilmaril@789
   119
	append (at(0));
insilmaril@789
   120
	for (int i=0;i<size()-1;i++)
insilmaril@789
   121
	{
insilmaril@789
   122
		p=at(i).x() * at(i+1).y() - at(i+1).x() * at(i).y();
insilmaril@789
   123
		_area+=p;
insilmaril@789
   124
		cx+=(at(i).x()+at(i+1).x()) * p;
insilmaril@789
   125
		cy+=(at(i).y()+at(i+1).y()) * p;
insilmaril@789
   126
	}	
insilmaril@789
   127
	pop_back();
insilmaril@789
   128
	// area is negative if vertices ordered counterclockwise
insilmaril@789
   129
	// (in mirrored graphicsview!)
insilmaril@789
   130
	_area=_area/2;	
insilmaril@789
   131
	p=_area*6;
insilmaril@789
   132
	_centroid.setX (cx/p);
insilmaril@789
   133
	_centroid.setY (cy/p);
insilmaril@789
   134
}
insilmaril@789
   135
insilmaril@789
   136
QPointF ConvexPolygon::centroid() const
insilmaril@789
   137
{
insilmaril@789
   138
	return _centroid;
insilmaril@789
   139
}
insilmaril@789
   140
insilmaril@789
   141
qreal ConvexPolygon::weight() const
insilmaril@789
   142
{
insilmaril@789
   143
	return _area;
insilmaril@789
   144
}
insilmaril@789
   145
insilmaril@792
   146
std::string ConvexPolygon::toStdString()
insilmaril@656
   147
{
insilmaril@792
   148
	QString s ("(");
insilmaril@792
   149
	for (int i=0;i<size();++i)
insilmaril@792
   150
	{
insilmaril@792
   151
		s+=QString("(%1,%2)").arg(at(i).x()).arg(at(i).y());
insilmaril@792
   152
		if (i<size()-1) s+=",";
insilmaril@792
   153
	}
insilmaril@792
   154
	s+=")";	
insilmaril@792
   155
	return s.toStdString();
insilmaril@656
   156
}
insilmaril@656
   157
insilmaril@792
   158
Vector ConvexPolygon::at(const int &i) const
insilmaril@656
   159
{
insilmaril@792
   160
	return Vector (QPolygonF::at(i).x(),QPolygonF::at(i).y());
insilmaril@656
   161
}
insilmaril@656
   162
insilmaril@792
   163
void ConvexPolygon::translate ( const Vector & offset )
insilmaril@792
   164
{ translate (offset.x(),offset.y());}
insilmaril@656
   165
insilmaril@792
   166
void ConvexPolygon::translate ( qreal dx, qreal dy )
insilmaril@789
   167
{
insilmaril@792
   168
	QPolygonF::translate (dx,dy);
insilmaril@792
   169
	_centroid=_centroid+QPointF (dx,dy);
insilmaril@789
   170
}
insilmaril@789
   171
insilmaril@792
   172
void projectPolygon(Vector axis, ConvexPolygon polygon, qreal &min, qreal &max) 
insilmaril@656
   173
{
insilmaril@656
   174
    // To project a point on an axis use the dot product
insilmaril@656
   175
insilmaril@789
   176
	//cout << "Projecting on "<< axis<<endl;
insilmaril@792
   177
    qreal d = axis.dotProduct(polygon.at(0));
insilmaril@656
   178
    min = d;
insilmaril@656
   179
    max = d;
insilmaril@789
   180
    for (int i = 0; i < polygon.size(); i++) 
insilmaril@789
   181
	{
insilmaril@792
   182
        d= polygon.at(i).dotProduct (axis);
insilmaril@656
   183
        if (d < min) 
insilmaril@656
   184
            min = d;
insilmaril@656
   185
        else 
insilmaril@656
   186
            if (d> max) max = d;
insilmaril@789
   187
	//	cout << "p="<<polygon.at(i)<<"  d="<<d<<"  (min, max)=("<<min<<","<<max<<")\n";	
insilmaril@656
   188
    }
insilmaril@656
   189
}
insilmaril@656
   190
insilmaril@789
   191
// Calculate the signed distance between [minA, maxA] and [minB, maxB]
insilmaril@789
   192
// The distance will be negative if the intervals overlap
insilmaril@656
   193
insilmaril@656
   194
qreal intervalDistance(qreal minA, qreal maxA, qreal minB, qreal maxB) {
insilmaril@656
   195
    if (minA < minB) {
insilmaril@656
   196
        return minB - maxA;
insilmaril@656
   197
    } else {
insilmaril@656
   198
        return minA - maxB;
insilmaril@656
   199
    }
insilmaril@656
   200
}
insilmaril@789
   201
insilmaril@789
   202
/*!
insilmaril@656
   203
 Check if polygon A is going to collide with polygon B.
insilmaril@656
   204
 The last parameter is the *relative* velocity 
insilmaril@656
   205
 of the polygons (i.e. velocityA - velocityB)
insilmaril@789
   206
*/
insilmaril@656
   207
insilmaril@792
   208
PolygonCollisionResult polygonCollision(ConvexPolygon polygonA, 
insilmaril@792
   209
                              ConvexPolygon polygonB, Vector velocity) 
insilmaril@789
   210
{
insilmaril@656
   211
    PolygonCollisionResult result;
insilmaril@662
   212
    result.intersect = true;
insilmaril@662
   213
    result.willIntersect = true;
insilmaril@656
   214
insilmaril@656
   215
    int edgeCountA = polygonA.size();
insilmaril@656
   216
    int edgeCountB = polygonB.size();
insilmaril@656
   217
    qreal minIntervalDistance = 1000000000;
insilmaril@656
   218
    QPointF translationAxis;
insilmaril@656
   219
    QPointF edge;
insilmaril@656
   220
insilmaril@789
   221
/*
insilmaril@662
   222
	cout << "\nA: ";
insilmaril@662
   223
	for (int k=0; k<edgeCountA;k++)
insilmaril@662
   224
		cout <<polygonA.at(k);
insilmaril@662
   225
	cout << "\nB: ";
insilmaril@662
   226
	for (int k=0; k<edgeCountB;k++)
insilmaril@662
   227
		cout <<polygonB.at(k);
insilmaril@789
   228
	cout <<endl;	
insilmaril@789
   229
*/		
insilmaril@662
   230
		
insilmaril@656
   231
    // Loop through all the edges of both polygons
insilmaril@789
   232
	for (int i=0;i<edgeCountA + edgeCountB;i++)
insilmaril@656
   233
	{
insilmaril@656
   234
        if (i< edgeCountA) 
insilmaril@662
   235
		{
insilmaril@789
   236
			// Loop through polygon A
insilmaril@789
   237
			if (i<edgeCountA-1)
insilmaril@662
   238
				edge = QPointF (
insilmaril@662
   239
					polygonA.at(i+1).x()-polygonA.at(i).x(), 
insilmaril@662
   240
					polygonA.at(i+1).y()-polygonA.at(i).y());
insilmaril@662
   241
			else		
insilmaril@662
   242
				edge = QPointF (
insilmaril@662
   243
					polygonA.at(0).x()-polygonA.at(i).x(), 
insilmaril@662
   244
					polygonA.at(0).y()-polygonA.at(i).y());
insilmaril@662
   245
        } else 
insilmaril@662
   246
		{
insilmaril@789
   247
			// Loop through polygon B
insilmaril@789
   248
			if (i < edgeCountA +edgeCountB -1 )
insilmaril@662
   249
				edge = QPointF (
insilmaril@789
   250
					polygonB.at(i-edgeCountA+1).x() - polygonB.at(i-edgeCountA).x(), 
insilmaril@789
   251
					polygonB.at(i-edgeCountA+1).y() - polygonB.at(i-edgeCountA).y());
insilmaril@662
   252
			else	
insilmaril@662
   253
				edge = QPointF (
insilmaril@789
   254
					polygonB.at(0).x() - polygonB.at(i-edgeCountA).x(), 
insilmaril@789
   255
					polygonB.at(0).y() - polygonB.at(i-edgeCountA).y());
insilmaril@662
   256
		}
insilmaril@656
   257
insilmaril@656
   258
        // ===== 1. Find if the polygons are currently intersecting =====
insilmaril@656
   259
insilmaril@656
   260
        // Find the axis perpendicular to the current edge
insilmaril@656
   261
insilmaril@792
   262
        Vector axis (-edge.y(), edge.x());
insilmaril@792
   263
        axis.normalize();
insilmaril@656
   264
insilmaril@656
   265
        // Find the projection of the polygon on the current axis
insilmaril@656
   266
insilmaril@656
   267
        qreal minA = 0; qreal minB = 0; qreal maxA = 0; qreal maxB = 0;
insilmaril@789
   268
        projectPolygon(axis, polygonA, minA, maxA);
insilmaril@789
   269
        projectPolygon(axis, polygonB, minB, maxB);
insilmaril@656
   270
insilmaril@656
   271
        // Check if the polygon projections are currentlty intersecting
insilmaril@656
   272
insilmaril@789
   273
        qreal d = intervalDistance(minA, maxA, minB, maxB);
insilmaril@789
   274
        if (d > 0) result.intersect = false;
insilmaril@656
   275
insilmaril@789
   276
       // ===== 2. Now find if the polygons *will* intersect =====
insilmaril@656
   277
insilmaril@656
   278
insilmaril@656
   279
        // Project the velocity on the current axis
insilmaril@656
   280
insilmaril@792
   281
        qreal velocityProjection = axis.dotProduct(velocity);
insilmaril@656
   282
insilmaril@656
   283
        // Get the projection of polygon A during the movement
insilmaril@656
   284
insilmaril@662
   285
        if (velocityProjection < 0) 
insilmaril@656
   286
            minA += velocityProjection;
insilmaril@662
   287
        else 
insilmaril@656
   288
            maxA += velocityProjection;
insilmaril@656
   289
insilmaril@656
   290
        // Do the same test as above for the new projection
insilmaril@656
   291
insilmaril@789
   292
        // d = intervalDistance(minA, maxA, minB, maxB);
insilmaril@789
   293
        //if (d > 0) result.willIntersect = false;
insilmaril@662
   294
		/*
insilmaril@662
   295
		cout <<"   ";
insilmaril@789
   296
		cout << "edge="<<edge<<"  ";
insilmaril@789
   297
		cout <<"axis="<<axis<<"  ";
insilmaril@789
   298
		cout <<"dA=("<<minA<<","<<maxA<<")  dB=("<<minB<<","<<maxB<<")";
insilmaril@662
   299
		cout <<"  d="<<d<<"   ";
insilmaril@789
   300
		//cout <<"minD="<<minIntervalDistance<<"  ";
insilmaril@662
   301
		cout <<"int="<<result.intersect<<"  ";
insilmaril@789
   302
		//cout <<"wint="<<result.willIntersect<<"  ";
insilmaril@662
   303
		//cout <<"velProj="<<velocityProjection<<"  ";
insilmaril@662
   304
		cout <<endl;
insilmaril@789
   305
		*/
insilmaril@662
   306
	
insilmaril@789
   307
        if (result.intersect )// || result.willIntersect) 
insilmaril@662
   308
		{
insilmaril@662
   309
			// Check if the current interval distance is the minimum one. If so
insilmaril@662
   310
			// store the interval distance and the current distance.  This will
insilmaril@662
   311
			// be used to calculate the minimum translation vector
insilmaril@656
   312
insilmaril@662
   313
			if (d<0) d=-d;
insilmaril@662
   314
			if (d < minIntervalDistance) {
insilmaril@662
   315
				minIntervalDistance = d;
insilmaril@789
   316
				//translationAxis = axis;
insilmaril@789
   317
				//cout << "tAxix="<<translationAxis<<endl;
insilmaril@656
   318
insilmaril@662
   319
				//QPointF t = polygonA.Center - polygonB.Center;
insilmaril@789
   320
				//QPointF t = polygonA.at(0) - polygonB.at(0);
insilmaril@789
   321
				//if (dotProduct(t,translationAxis) < 0)
insilmaril@789
   322
				//	translationAxis = -translationAxis;
insilmaril@662
   323
			}
insilmaril@662
   324
		}
insilmaril@656
   325
    }
insilmaril@656
   326
insilmaril@656
   327
    // The minimum translation vector
insilmaril@656
   328
    // can be used to push the polygons appart.
insilmaril@656
   329
insilmaril@662
   330
    if (result.willIntersect)
insilmaril@662
   331
        result.minTranslation = 
insilmaril@656
   332
               translationAxis * minIntervalDistance;
insilmaril@656
   333
    
insilmaril@656
   334
    return result;
insilmaril@656
   335
}
insilmaril@656
   336
insilmaril@656
   337
/* The function can be used this way: 
insilmaril@656
   338
   QPointF polygonATranslation = new QPointF();
insilmaril@656
   339
*/   
insilmaril@656
   340
insilmaril@656
   341
insilmaril@656
   342
/*
insilmaril@656
   343
PolygonCollisionResult r = PolygonCollision(polygonA, polygonB, velocity);
insilmaril@656
   344
insilmaril@656
   345
if (r.WillIntersect) 
insilmaril@656
   346
  // Move the polygon by its velocity, then move
insilmaril@656
   347
  // the polygons appart using the Minimum Translation Vector
insilmaril@662
   348
  polygonATranslation = velocity + r.minTranslation;
insilmaril@656
   349
else 
insilmaril@656
   350
  // Just move the polygon by its velocity
insilmaril@656
   351
  polygonATranslation = velocity;
insilmaril@656
   352
insilmaril@656
   353
polygonA.Offset(polygonATranslation);
insilmaril@656
   354
insilmaril@656
   355
*/
insilmaril@656
   356
insilmaril@656
   357