geometry.cpp
author insilmaril
Fri, 01 Feb 2008 15:28:35 +0000
changeset 661 6a5e2c27f8a4
parent 656 53ef954e90b6
child 662 9abdd5e6c3dc
permissions -rw-r--r--
Added brasilian translation by Amadeu Júnior
insilmaril@650
     1
#include "geometry.h"
insilmaril@650
     2
insilmaril@656
     3
#include <math.h>
insilmaril@656
     4
insilmaril@650
     5
insilmaril@650
     6
QRectF addBBox(QRectF r1, QRectF r2)
insilmaril@650
     7
{	
insilmaril@650
     8
	// Find smallest QRectF containing given rectangles
insilmaril@650
     9
insilmaril@650
    10
	QRectF n;
insilmaril@650
    11
	// Set left border
insilmaril@650
    12
	if (r1.left() <= r2.left() )
insilmaril@650
    13
		n.setLeft(r1.left() );
insilmaril@650
    14
	else
insilmaril@650
    15
		n.setLeft(r2.left() );
insilmaril@650
    16
		
insilmaril@650
    17
	// Set top border		
insilmaril@650
    18
	if (r1.top() <= r2.top() )
insilmaril@650
    19
		n.setTop(r1.top() );
insilmaril@650
    20
	else
insilmaril@650
    21
		n.setTop(r2.top() );
insilmaril@650
    22
		
insilmaril@650
    23
	// Set right border
insilmaril@650
    24
	if (r1.right() <= r2.right() )
insilmaril@650
    25
		n.setRight(r2.right() );
insilmaril@650
    26
	else
insilmaril@650
    27
		n.setRight(r1.right() );
insilmaril@650
    28
		
insilmaril@650
    29
	// Set bottom 
insilmaril@650
    30
	if (r1.bottom() <= r2.bottom() )
insilmaril@650
    31
		n.setBottom(r2.bottom() );
insilmaril@650
    32
	else
insilmaril@650
    33
		n.setBottom(r1.bottom() );
insilmaril@650
    34
	return n;
insilmaril@650
    35
}
insilmaril@650
    36
insilmaril@650
    37
bool inBox(const QPointF &p, const QRectF &box)
insilmaril@650
    38
{
insilmaril@650
    39
    if (p.x() >= box.left() && p.x() <= box.right()  
insilmaril@650
    40
	&& p.y() <= box.bottom() && p.y() >= box.top() )
insilmaril@650
    41
		return true;
insilmaril@650
    42
    return false;	
insilmaril@650
    43
}
insilmaril@656
    44
insilmaril@656
    45
QPointF normalize (const QPointF &p)
insilmaril@656
    46
{
insilmaril@656
    47
	qreal n=sqrt ( p.x()*p.x() + p.y()*p.y() );
insilmaril@656
    48
	return QPointF (p.x()/n,p.y()/n);
insilmaril@656
    49
}
insilmaril@656
    50
insilmaril@656
    51
// Dot product of two vectors
insilmaril@656
    52
qreal dotProduct (const QPointF &a, const QPointF &b)
insilmaril@656
    53
{
insilmaril@656
    54
	return a.x()*b.x() + a.y()*b.y();
insilmaril@656
    55
}
insilmaril@656
    56
insilmaril@656
    57
// Structure that stores the results of the PolygonCollision function
insilmaril@656
    58
class PolygonCollisionResult {
insilmaril@656
    59
public:
insilmaril@656
    60
    // Are the polygons going to intersect forward in time?
insilmaril@656
    61
    bool WillIntersect;
insilmaril@656
    62
insilmaril@656
    63
    // Are the polygons currently intersecting?
insilmaril@656
    64
    bool Intersect;
insilmaril@656
    65
insilmaril@656
    66
    // The translation to apply to the first polygon to push the polygons apart.
insilmaril@656
    67
    QPointF MinimumTranslationVector;
insilmaril@656
    68
};
insilmaril@656
    69
insilmaril@656
    70
insilmaril@656
    71
/* Calculate the projection of a polygon on an axis
insilmaril@656
    72
   and returns it as a [min, max] interval
insilmaril@656
    73
*/
insilmaril@656
    74
void ProjectPolygon(QPointF axis, QPolygonF polygon, qreal &min, qreal &max) 
insilmaril@656
    75
{
insilmaril@656
    76
    // To project a point on an axis use the dot product
insilmaril@656
    77
insilmaril@656
    78
    qreal d = dotProduct(axis,polygon.at(0));
insilmaril@656
    79
    min = d;
insilmaril@656
    80
    max = d;
insilmaril@656
    81
    for (int i = 0; i < polygon.size(); i++) {
insilmaril@656
    82
        d= dotProduct (polygon.at(i),axis);
insilmaril@656
    83
        if (d < min) 
insilmaril@656
    84
            min = d;
insilmaril@656
    85
        else 
insilmaril@656
    86
		{
insilmaril@656
    87
            if (d> max) max = d;
insilmaril@656
    88
        }
insilmaril@656
    89
    }
insilmaril@656
    90
}
insilmaril@656
    91
insilmaril@656
    92
/* Calculate the signed distance between [minA, maxA] and [minB, maxB]
insilmaril@656
    93
   The distance will be negative if the intervals overlap
insilmaril@656
    94
*/
insilmaril@656
    95
insilmaril@656
    96
insilmaril@656
    97
qreal intervalDistance(qreal minA, qreal maxA, qreal minB, qreal maxB) {
insilmaril@656
    98
    if (minA < minB) {
insilmaril@656
    99
        return minB - maxA;
insilmaril@656
   100
    } else {
insilmaril@656
   101
        return minA - maxB;
insilmaril@656
   102
    }
insilmaril@656
   103
}
insilmaril@656
   104
/*
insilmaril@656
   105
 Check if polygon A is going to collide with polygon B.
insilmaril@656
   106
 The last parameter is the *relative* velocity 
insilmaril@656
   107
 of the polygons (i.e. velocityA - velocityB)
insilmaril@656
   108
insilmaril@656
   109
*/
insilmaril@656
   110
PolygonCollisionResult PolygonCollision(QPolygonF polygonA, 
insilmaril@656
   111
                              QPolygonF polygonB, QPointF velocity) {
insilmaril@656
   112
    PolygonCollisionResult result;
insilmaril@656
   113
    result.Intersect = true;
insilmaril@656
   114
    result.WillIntersect = true;
insilmaril@656
   115
insilmaril@656
   116
    int edgeCountA = polygonA.size();
insilmaril@656
   117
    int edgeCountB = polygonB.size();
insilmaril@656
   118
    qreal minIntervalDistance = 1000000000;
insilmaril@656
   119
    QPointF translationAxis;
insilmaril@656
   120
    QPointF edge;
insilmaril@656
   121
insilmaril@656
   122
    // Loop through all the edges of both polygons
insilmaril@656
   123
insilmaril@656
   124
    for (int i=0; i < edgeCountA + edgeCountB; i++) 
insilmaril@656
   125
	{
insilmaril@656
   126
        if (i< edgeCountA) 
insilmaril@656
   127
            edge = polygonA.at(i);
insilmaril@656
   128
        else 
insilmaril@656
   129
            edge = polygonB.at(i - edgeCountA);
insilmaril@656
   130
insilmaril@656
   131
        // ===== 1. Find if the polygons are currently intersecting =====
insilmaril@656
   132
insilmaril@656
   133
insilmaril@656
   134
        // Find the axis perpendicular to the current edge
insilmaril@656
   135
insilmaril@656
   136
        QPointF axis (-edge.y(), edge.x());
insilmaril@656
   137
        normalize(axis);
insilmaril@656
   138
insilmaril@656
   139
        // Find the projection of the polygon on the current axis
insilmaril@656
   140
insilmaril@656
   141
        qreal minA = 0; qreal minB = 0; qreal maxA = 0; qreal maxB = 0;
insilmaril@656
   142
        ProjectPolygon(axis, polygonA, minA, maxA);
insilmaril@656
   143
        ProjectPolygon(axis, polygonB, minB, maxB);
insilmaril@656
   144
insilmaril@656
   145
        // Check if the polygon projections are currentlty intersecting
insilmaril@656
   146
insilmaril@656
   147
        if (intervalDistance(minA, maxA, minB, maxB) > 0)\
insilmaril@656
   148
            result.Intersect = false;
insilmaril@656
   149
insilmaril@656
   150
        // ===== 2. Now find if the polygons *will* intersect =====
insilmaril@656
   151
insilmaril@656
   152
insilmaril@656
   153
        // Project the velocity on the current axis
insilmaril@656
   154
insilmaril@656
   155
        qreal velocityProjection = dotProduct(axis,velocity);
insilmaril@656
   156
insilmaril@656
   157
        // Get the projection of polygon A during the movement
insilmaril@656
   158
insilmaril@656
   159
        if (velocityProjection < 0) {
insilmaril@656
   160
            minA += velocityProjection;
insilmaril@656
   161
        } else {
insilmaril@656
   162
            maxA += velocityProjection;
insilmaril@656
   163
        }
insilmaril@656
   164
insilmaril@656
   165
        // Do the same test as above for the new projection
insilmaril@656
   166
insilmaril@656
   167
        qreal d = intervalDistance(minA, maxA, minB, maxB);
insilmaril@656
   168
        if (d > 0) result.WillIntersect = false;
insilmaril@656
   169
insilmaril@656
   170
        // If the polygons are not intersecting and won't intersect, exit the loop
insilmaril@656
   171
insilmaril@656
   172
        if (!result.Intersect && !result.WillIntersect) break;
insilmaril@656
   173
insilmaril@656
   174
        // Check if the current interval distance is the minimum one. If so store
insilmaril@656
   175
        // the interval distance and the current distance.
insilmaril@656
   176
        // This will be used to calculate the minimum translation vector
insilmaril@656
   177
insilmaril@656
   178
        if (d<0) d=-d;
insilmaril@656
   179
        if (d < minIntervalDistance) {
insilmaril@656
   180
            minIntervalDistance = d;
insilmaril@656
   181
            translationAxis = axis;
insilmaril@656
   182
insilmaril@656
   183
            //QPointF t = polygonA.Center - polygonB.Center;
insilmaril@656
   184
            QPointF t = polygonA.at(0) - polygonB.at(0);
insilmaril@656
   185
            if (dotProduct(t,translationAxis) < 0)
insilmaril@656
   186
                translationAxis = -translationAxis;
insilmaril@656
   187
        }
insilmaril@656
   188
    }
insilmaril@656
   189
insilmaril@656
   190
    // The minimum translation vector
insilmaril@656
   191
    // can be used to push the polygons appart.
insilmaril@656
   192
insilmaril@656
   193
    if (result.WillIntersect)
insilmaril@656
   194
        result.MinimumTranslationVector = 
insilmaril@656
   195
               translationAxis * minIntervalDistance;
insilmaril@656
   196
    
insilmaril@656
   197
    return result;
insilmaril@656
   198
}
insilmaril@656
   199
insilmaril@656
   200
/* The function can be used this way: 
insilmaril@656
   201
   QPointF polygonATranslation = new QPointF();
insilmaril@656
   202
*/   
insilmaril@656
   203
insilmaril@656
   204
insilmaril@656
   205
/*
insilmaril@656
   206
PolygonCollisionResult r = PolygonCollision(polygonA, polygonB, velocity);
insilmaril@656
   207
insilmaril@656
   208
if (r.WillIntersect) 
insilmaril@656
   209
  // Move the polygon by its velocity, then move
insilmaril@656
   210
  // the polygons appart using the Minimum Translation Vector
insilmaril@656
   211
  polygonATranslation = velocity + r.MinimumTranslationVector;
insilmaril@656
   212
else 
insilmaril@656
   213
  // Just move the polygon by its velocity
insilmaril@656
   214
  polygonATranslation = velocity;
insilmaril@656
   215
insilmaril@656
   216
polygonA.Offset(polygonATranslation);
insilmaril@656
   217
insilmaril@656
   218
*/
insilmaril@656
   219
insilmaril@656
   220