| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423 |
- /*
- ** Command & Conquer Generals(tm)
- ** Copyright 2025 Electronic Arts Inc.
- **
- ** This program is free software: you can redistribute it and/or modify
- ** it under the terms of the GNU General Public License as published by
- ** the Free Software Foundation, either version 3 of the License, or
- ** (at your option) any later version.
- **
- ** This program is distributed in the hope that it will be useful,
- ** but WITHOUT ANY WARRANTY; without even the implied warranty of
- ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- ** GNU General Public License for more details.
- **
- ** You should have received a copy of the GNU General Public License
- ** along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- ////////////////////////////////////////////////////////////////////////////////
- // //
- // (c) 2001-2003 Electronic Arts Inc. //
- // //
- ////////////////////////////////////////////////////////////////////////////////
- // FILE: Line2D.cpp ///////////////////////////////////////////////////////////////////////////////
- // Author: Colin Day, January 2002
- // Desc: Some helpful 2D stuff
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- // INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
- #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
- #include "Lib/BaseType.h"
- #include "GameClient/Line2D.h"
- // PRIVATE ////////////////////////////////////////////////////////////////////////////////////////
- #define CLIP_LEFT 0x01
- #define CLIP_RIGHT 0x02
- #define CLIP_BOTTOM 0x04
- #define CLIP_TOP 0x08
- // PUBLIC /////////////////////////////////////////////////////////////////////////////////////////
- // STATIC /////////////////////////////////////////////////////////////////////////////////////////
- const static Coord2D reallyFarPoint = { 1000000.0, 1000000.0 };
- //-------------------------------------------------------------------------------------------------
- /** Clip a line to the region provided. The source line runs from p1 to p2, and is clipped
- * using the clipRegion. The resulting line goes from c1 to c2
- *
- * Return values:
- * TRUE - Line is visible
- * FALSE - Line is not visible
- */
- //-------------------------------------------------------------------------------------------------
- Bool ClipLine2D( ICoord2D *p1, ICoord2D *p2, ICoord2D *c1, ICoord2D *c2,
- IRegion2D *clipRegion )
- {
- Int x1, y1, x2, y2;
- Int clipLeft;
- Int clipRight;
- Int clipTop;
- Int clipBottom;
- Int clipCode1;
- Int clipCode2;
- Int diff;
- // Use clip window that includes bottom right pixel
- clipLeft = clipRegion->lo.x;
- clipRight = clipRegion->hi.x;
- clipTop = clipRegion->lo.y;
- clipBottom = clipRegion->hi.y;
- /*
- clipLeft = gfxCurrentContext->clipRect1.left;
- clipRight = gfxCurrentContext->clipRect1.right;
- clipTop = gfxCurrentContext->clipRect1.top;
- clipBottom = gfxCurrentContext->clipRect1.bottom;
- x1 = *px1;
- y1 = *py1;
- x2 = *px2;
- y2 = *py2;
- */
- x1 = p1->x;
- y1 = p1->y;
- x2 = p2->x;
- y2 = p2->y;
-
- // Test first point
- clipCode1 = 0;
- if (x1 < clipLeft)
- clipCode1 = CLIP_LEFT;
- else
- if (x1 > clipRight)
- clipCode1 = CLIP_RIGHT;
- if (y1 < clipTop)
- clipCode1 |= CLIP_TOP;
- else
- if (y1 > clipBottom)
- clipCode1 |= CLIP_BOTTOM;
- // Test second point
- clipCode2 = 0;
- if (x2 < clipLeft)
- clipCode2 = CLIP_LEFT;
- else
- if (x2 > clipRight)
- clipCode2 = CLIP_RIGHT;
- if (y2 < clipTop)
- clipCode2 |= CLIP_TOP;
- else
- if (y2 > clipBottom)
- clipCode2 |= CLIP_BOTTOM;
- // Both points inside window?
- if ((clipCode1 | clipCode2) == 0)
- {
- *c1 = *p1;
- *c2 = *p2;
- return TRUE;
- } // end if
- // Both points outside window?
- if (clipCode1 & clipCode2)
- return FALSE;
- // First point outside window?
- if (clipCode1)
- {
- if (clipCode1 & CLIP_TOP)
- {
- if ((diff = (y2 - y1)) == 0)
- return FALSE;
- x1 += (x2 - x1) * (clipTop - y1) / diff;
- y1 = clipTop;
- }
- else
- if (clipCode1 & CLIP_BOTTOM)
- {
- if ((diff = (y2 - y1)) == 0)
- return FALSE;
- x1 += (x2 - x1) * (clipBottom - y1) / diff;
- y1 = clipBottom;
- }
- if (x1 > clipRight)
- {
- if ((diff = (x2 - x1)) == 0)
- return FALSE;
- y1 += (y2 - y1) * (clipRight - x1) / diff;
- x1 = clipRight;
- }
- else
- if (x1 < clipLeft)
- {
- if ((diff = (x2 - x1)) == 0)
- return FALSE;
- y1 += (y2 - y1) * (clipLeft - x1) / diff;
- x1 = clipLeft;
- }
- }
- // Second point outside window?
- if (clipCode2)
- {
- if (clipCode2 & CLIP_TOP)
- {
- if ((diff = (y2 - y1)) == 0)
- return FALSE;
- x2 += (x2 - x1) * (clipTop - y2) / diff;
- y2 = clipTop;
- }
- else
- if (clipCode2 & CLIP_BOTTOM)
- {
- if ((diff = (y2 - y1)) == 0)
- return FALSE;
- x2 += (x2 - x1) * (clipBottom - y2) / diff;
- y2 = clipBottom;
- }
- if (x2 > clipRight)
- {
- if ((diff = (x2 - x1)) == 0)
- return FALSE;
- y2 += (y2 - y1) * (clipRight - x2) / diff;
- x2 = clipRight;
- }
- else
- if (x2 < clipLeft)
- {
- if ((diff = (x2 - x1)) == 0)
- return FALSE;
- y2 += (y2 - y1) * (clipLeft - x2) / diff;
- x2 = clipLeft;
- }
- }
- c1->x = x1;
- c1->y = y1;
- c2->x = x2;
- c2->y = y2;
- /*
- *px1 = x1;
- *py1 = y1;
- *px2 = x2;
- *py2 = y2;
- */
- // Line is visible
- return (x1 >= clipLeft && x1 <= clipRight &&
- y1 >= clipTop && y1 <= clipBottom &&
- x2 >= clipLeft && x2 <= clipRight &&
- y2 >= clipTop && y2 <= clipBottom);
- } // end ClipLine2D
- // This solution uses the
- // http://www.faqs.org/faqs/graphics/algorithms-faq/
- // Subject 1.03
- Bool IntersectLine2D( const Coord2D *a, const Coord2D *b,
- const Coord2D *c, const Coord2D *d,
- Coord2D *intersection)
- {
- if (!a || !b || !c || !d) {
- // sanity. Lines that do not have endpoints do not intersect.
- return false;
- }
- Real r, s, denom;
- denom = ((b->x - a->x) * (d->y - c->y) - (b->y - a->y) * (d->x - c->x));
- if (denom == 0) {
- // the lines are parallel.
- return false;
- }
- r = ((a->y - c->y) * (d->x - c->x) - (a->x - c->x) * (d->y - c->y) ) / denom;
- s = ((a->y - c->y) * (b->x - a->x) - (a->x - c->x) * (b->y - a->y) ) / denom;
- if (0 <= r && r <= 1 && 0 <= s && s <= 1) {
- // The lines intersect.
- if (intersection) {
- intersection->x = a->x + r * (b->x - a->x);
- intersection->y = a->y + r * (b->y - a->y);
- }
- return true;
- }
- return false;
- }
- // determines whether a point lies within a rectangle. Doesnt' determine whether the shape is
- // actually a rectangle or not.
- Bool PointInsideRect2D(const Coord2D *bl, const Coord2D *tl, const Coord2D *br, const Coord2D *tr,
- const Coord2D *inputPoint)
- {
- if (!(bl && br && tl && tr && inputPoint)) {
- return FALSE;
- }
- Real uVal;
- // we're actually only interested in if the U value is (0,1)
- ShortestDistancePointToSegment2D(bl, tl, inputPoint, NULL, NULL, &uVal);
- if (uVal <= 0.0f || uVal >= 1.0f) {
- return false;
- }
- ShortestDistancePointToSegment2D(bl, br, inputPoint, NULL, NULL, &uVal);
- return (uVal > 0.0f && uVal < 1.0f);
- }
- // convenience. Just prunes out the Z coordinate for a call to PointInsideRect2D
- Bool PointInsideRect3D(const Coord3D *bl, const Coord3D *tl, const Coord3D *br, const Coord3D *tr,
- const Coord3D *inputPoint)
- {
- Coord2D bl2d, tl2d, br2d, tr2d, pt;
- bl2d.x = bl->x;
- bl2d.y = bl->y;
- tl2d.x = tl->x;
- tl2d.y = tl->y;
- br2d.x = br->x;
- br2d.y = br->y;
- tr2d.x = tr->x;
- tr2d.y = tr->y;
- pt.x = inputPoint->x;
- pt.y = inputPoint->y;
- return PointInsideRect2D(&bl2d, &br2d, &tl2d, &tr2d, &pt);
- }
- // This function uses even-odd winding to determine whether a point is inside an area.
- Bool PointInsideArea2D(const Coord2D *ptToTest, const Coord2D *area, const Int numPointsInArea)
- {
- int numIntersections = 0;
- for (int i = 0; i < numPointsInArea; ++i) {
- if (IntersectLine2D(ptToTest, &reallyFarPoint, &area[i], &area[(i + 1) % numPointsInArea])) {
- ++numIntersections;
- }
- }
- return (numIntersections % 2 == 1);
- }
- // This function uses even-odd winding to determine whether a point is inside an area.
- Bool PointInsideArea2D( const Coord3D *ptToTest, const Coord3D *area, Int numPointsInArea)
- {
- int numIntersections = 0;
- Coord2D pt2D, area2D1, area2D2;
- pt2D.x = ptToTest->x;
- pt2D.y = ptToTest->y;
- for (int i = 0; i < numPointsInArea; ++i) {
- area2D1.x = area[i].x;
- area2D1.y = area[i].y;
- area2D2.x = area[(i + 1) % numPointsInArea].x;
- area2D2.y = area[(i + 1) % numPointsInArea].y;
- if (IntersectLine2D(&pt2D, &reallyFarPoint, &area2D1, &area2D2)) {
- ++numIntersections;
- }
- }
- return (numIntersections % 2 == 1);
- }
- ///< Checks if a point is inside a perfect rectangle (top left and bottom right)
- Bool Coord3DInsideRect2D( const Coord3D *inputPoint, const Coord2D *tl, const Coord2D *br )
- {
- if( inputPoint->x >= tl->x && inputPoint->x <= br->x )
- {
- if( inputPoint->y >= tl->y && inputPoint->y <= br->y )
- {
- return TRUE;
- }
- }
- return FALSE;
- }
- ///< Scales a rect by a factor either growing or shrinking it.
- void ScaleRect2D( Coord2D *tl, Coord2D *br, Real scaleFactor )
- {
- scaleFactor = scaleFactor-1.0f; // We are starting with tl,br, so scaling it by 1 means adding 0 to it.
- Real deltaWidth = (br->x - tl->x) * scaleFactor * 0.5f;
- Real deltaHeight = (br->y - tl->y) * scaleFactor * 0.5f;
- tl->x -= deltaWidth;
- tl->y -= deltaHeight;
- br->x += deltaWidth;
- br->y += deltaHeight;
- }
- // Solution taken from http://astronomy.swin.edu.au/~pbourke/geometry/pointline/
- void ShortestDistancePointToSegment2D( const Coord2D *a, const Coord2D *b, const Coord2D *pt,
- Real *outDistance, Coord2D *outPosition, Real *outU )
- {
- if (!a || !b || !pt) {
- return;
- }
- if (a->x == b->x && a->y == b->y) {
- // special case, its simply pt to pt.
- Coord2D segment;
- segment.x = pt->x - a->x;
- segment.y = pt->y - a->y;
- if (outDistance) {
- (*outDistance) = segment.length();
- }
- if (outPosition) {
- (*outPosition).x = a->x;
- (*outPosition).y = a->y;
- }
- if (outU) {
- (*outU) = 0.5;
- }
- return;
- }
- Coord2D segAB;
- segAB.x = b->x - a->x;
- segAB.y = b->y - a->y;
- // General case
- Real u = ((pt->x - a->x) * (b->x - a->x) + (pt->y - a->y) * (b->y - a->y)) /
- sqr(segAB.length());
- Coord2D intersectSegment;
- intersectSegment.x = a->x + u * (b->x - a->x);
- intersectSegment.y = a->y + u * (b->y - a->y);
- if (outPosition) {
- (*outPosition) = intersectSegment;
- }
- if (outDistance) {
- (*outDistance) = intersectSegment.length();
- }
-
- if (outU) {
- (*outU) = u;
- }
- }
|