Line2D.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423
  1. /*
  2. ** Command & Conquer Generals Zero Hour(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. ////////////////////////////////////////////////////////////////////////////////
  19. // //
  20. // (c) 2001-2003 Electronic Arts Inc. //
  21. // //
  22. ////////////////////////////////////////////////////////////////////////////////
  23. // FILE: Line2D.cpp ///////////////////////////////////////////////////////////////////////////////
  24. // Author: Colin Day, January 2002
  25. // Desc: Some helpful 2D stuff
  26. ///////////////////////////////////////////////////////////////////////////////////////////////////
  27. // INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
  28. #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
  29. #include "Lib/BaseType.h"
  30. #include "GameClient/Line2D.h"
  31. // PRIVATE ////////////////////////////////////////////////////////////////////////////////////////
  32. #define CLIP_LEFT 0x01
  33. #define CLIP_RIGHT 0x02
  34. #define CLIP_BOTTOM 0x04
  35. #define CLIP_TOP 0x08
  36. // PUBLIC /////////////////////////////////////////////////////////////////////////////////////////
  37. // STATIC /////////////////////////////////////////////////////////////////////////////////////////
  38. const static Coord2D reallyFarPoint = { 1000000.0, 1000000.0 };
  39. //-------------------------------------------------------------------------------------------------
  40. /** Clip a line to the region provided. The source line runs from p1 to p2, and is clipped
  41. * using the clipRegion. The resulting line goes from c1 to c2
  42. *
  43. * Return values:
  44. * TRUE - Line is visible
  45. * FALSE - Line is not visible
  46. */
  47. //-------------------------------------------------------------------------------------------------
  48. Bool ClipLine2D( ICoord2D *p1, ICoord2D *p2, ICoord2D *c1, ICoord2D *c2,
  49. IRegion2D *clipRegion )
  50. {
  51. Int x1, y1, x2, y2;
  52. Int clipLeft;
  53. Int clipRight;
  54. Int clipTop;
  55. Int clipBottom;
  56. Int clipCode1;
  57. Int clipCode2;
  58. Int diff;
  59. // Use clip window that includes bottom right pixel
  60. clipLeft = clipRegion->lo.x;
  61. clipRight = clipRegion->hi.x;
  62. clipTop = clipRegion->lo.y;
  63. clipBottom = clipRegion->hi.y;
  64. /*
  65. clipLeft = gfxCurrentContext->clipRect1.left;
  66. clipRight = gfxCurrentContext->clipRect1.right;
  67. clipTop = gfxCurrentContext->clipRect1.top;
  68. clipBottom = gfxCurrentContext->clipRect1.bottom;
  69. x1 = *px1;
  70. y1 = *py1;
  71. x2 = *px2;
  72. y2 = *py2;
  73. */
  74. x1 = p1->x;
  75. y1 = p1->y;
  76. x2 = p2->x;
  77. y2 = p2->y;
  78. // Test first point
  79. clipCode1 = 0;
  80. if (x1 < clipLeft)
  81. clipCode1 = CLIP_LEFT;
  82. else
  83. if (x1 > clipRight)
  84. clipCode1 = CLIP_RIGHT;
  85. if (y1 < clipTop)
  86. clipCode1 |= CLIP_TOP;
  87. else
  88. if (y1 > clipBottom)
  89. clipCode1 |= CLIP_BOTTOM;
  90. // Test second point
  91. clipCode2 = 0;
  92. if (x2 < clipLeft)
  93. clipCode2 = CLIP_LEFT;
  94. else
  95. if (x2 > clipRight)
  96. clipCode2 = CLIP_RIGHT;
  97. if (y2 < clipTop)
  98. clipCode2 |= CLIP_TOP;
  99. else
  100. if (y2 > clipBottom)
  101. clipCode2 |= CLIP_BOTTOM;
  102. // Both points inside window?
  103. if ((clipCode1 | clipCode2) == 0)
  104. {
  105. *c1 = *p1;
  106. *c2 = *p2;
  107. return TRUE;
  108. } // end if
  109. // Both points outside window?
  110. if (clipCode1 & clipCode2)
  111. return FALSE;
  112. // First point outside window?
  113. if (clipCode1)
  114. {
  115. if (clipCode1 & CLIP_TOP)
  116. {
  117. if ((diff = (y2 - y1)) == 0)
  118. return FALSE;
  119. x1 += (x2 - x1) * (clipTop - y1) / diff;
  120. y1 = clipTop;
  121. }
  122. else
  123. if (clipCode1 & CLIP_BOTTOM)
  124. {
  125. if ((diff = (y2 - y1)) == 0)
  126. return FALSE;
  127. x1 += (x2 - x1) * (clipBottom - y1) / diff;
  128. y1 = clipBottom;
  129. }
  130. if (x1 > clipRight)
  131. {
  132. if ((diff = (x2 - x1)) == 0)
  133. return FALSE;
  134. y1 += (y2 - y1) * (clipRight - x1) / diff;
  135. x1 = clipRight;
  136. }
  137. else
  138. if (x1 < clipLeft)
  139. {
  140. if ((diff = (x2 - x1)) == 0)
  141. return FALSE;
  142. y1 += (y2 - y1) * (clipLeft - x1) / diff;
  143. x1 = clipLeft;
  144. }
  145. }
  146. // Second point outside window?
  147. if (clipCode2)
  148. {
  149. if (clipCode2 & CLIP_TOP)
  150. {
  151. if ((diff = (y2 - y1)) == 0)
  152. return FALSE;
  153. x2 += (x2 - x1) * (clipTop - y2) / diff;
  154. y2 = clipTop;
  155. }
  156. else
  157. if (clipCode2 & CLIP_BOTTOM)
  158. {
  159. if ((diff = (y2 - y1)) == 0)
  160. return FALSE;
  161. x2 += (x2 - x1) * (clipBottom - y2) / diff;
  162. y2 = clipBottom;
  163. }
  164. if (x2 > clipRight)
  165. {
  166. if ((diff = (x2 - x1)) == 0)
  167. return FALSE;
  168. y2 += (y2 - y1) * (clipRight - x2) / diff;
  169. x2 = clipRight;
  170. }
  171. else
  172. if (x2 < clipLeft)
  173. {
  174. if ((diff = (x2 - x1)) == 0)
  175. return FALSE;
  176. y2 += (y2 - y1) * (clipLeft - x2) / diff;
  177. x2 = clipLeft;
  178. }
  179. }
  180. c1->x = x1;
  181. c1->y = y1;
  182. c2->x = x2;
  183. c2->y = y2;
  184. /*
  185. *px1 = x1;
  186. *py1 = y1;
  187. *px2 = x2;
  188. *py2 = y2;
  189. */
  190. // Line is visible
  191. return (x1 >= clipLeft && x1 <= clipRight &&
  192. y1 >= clipTop && y1 <= clipBottom &&
  193. x2 >= clipLeft && x2 <= clipRight &&
  194. y2 >= clipTop && y2 <= clipBottom);
  195. } // end ClipLine2D
  196. // This solution uses the
  197. // http://www.faqs.org/faqs/graphics/algorithms-faq/
  198. // Subject 1.03
  199. Bool IntersectLine2D( const Coord2D *a, const Coord2D *b,
  200. const Coord2D *c, const Coord2D *d,
  201. Coord2D *intersection)
  202. {
  203. if (!a || !b || !c || !d) {
  204. // sanity. Lines that do not have endpoints do not intersect.
  205. return false;
  206. }
  207. Real r, s, denom;
  208. denom = ((b->x - a->x) * (d->y - c->y) - (b->y - a->y) * (d->x - c->x));
  209. if (denom == 0) {
  210. // the lines are parallel.
  211. return false;
  212. }
  213. r = ((a->y - c->y) * (d->x - c->x) - (a->x - c->x) * (d->y - c->y) ) / denom;
  214. s = ((a->y - c->y) * (b->x - a->x) - (a->x - c->x) * (b->y - a->y) ) / denom;
  215. if (0 <= r && r <= 1 && 0 <= s && s <= 1) {
  216. // The lines intersect.
  217. if (intersection) {
  218. intersection->x = a->x + r * (b->x - a->x);
  219. intersection->y = a->y + r * (b->y - a->y);
  220. }
  221. return true;
  222. }
  223. return false;
  224. }
  225. // determines whether a point lies within a rectangle. Doesnt' determine whether the shape is
  226. // actually a rectangle or not.
  227. Bool PointInsideRect2D(const Coord2D *bl, const Coord2D *tl, const Coord2D *br, const Coord2D *tr,
  228. const Coord2D *inputPoint)
  229. {
  230. if (!(bl && br && tl && tr && inputPoint)) {
  231. return FALSE;
  232. }
  233. Real uVal;
  234. // we're actually only interested in if the U value is (0,1)
  235. ShortestDistancePointToSegment2D(bl, tl, inputPoint, NULL, NULL, &uVal);
  236. if (uVal <= 0.0f || uVal >= 1.0f) {
  237. return false;
  238. }
  239. ShortestDistancePointToSegment2D(bl, br, inputPoint, NULL, NULL, &uVal);
  240. return (uVal > 0.0f && uVal < 1.0f);
  241. }
  242. // convenience. Just prunes out the Z coordinate for a call to PointInsideRect2D
  243. Bool PointInsideRect3D(const Coord3D *bl, const Coord3D *tl, const Coord3D *br, const Coord3D *tr,
  244. const Coord3D *inputPoint)
  245. {
  246. Coord2D bl2d, tl2d, br2d, tr2d, pt;
  247. bl2d.x = bl->x;
  248. bl2d.y = bl->y;
  249. tl2d.x = tl->x;
  250. tl2d.y = tl->y;
  251. br2d.x = br->x;
  252. br2d.y = br->y;
  253. tr2d.x = tr->x;
  254. tr2d.y = tr->y;
  255. pt.x = inputPoint->x;
  256. pt.y = inputPoint->y;
  257. return PointInsideRect2D(&bl2d, &br2d, &tl2d, &tr2d, &pt);
  258. }
  259. // This function uses even-odd winding to determine whether a point is inside an area.
  260. Bool PointInsideArea2D(const Coord2D *ptToTest, const Coord2D *area, const Int numPointsInArea)
  261. {
  262. int numIntersections = 0;
  263. for (int i = 0; i < numPointsInArea; ++i) {
  264. if (IntersectLine2D(ptToTest, &reallyFarPoint, &area[i], &area[(i + 1) % numPointsInArea])) {
  265. ++numIntersections;
  266. }
  267. }
  268. return (numIntersections % 2 == 1);
  269. }
  270. // This function uses even-odd winding to determine whether a point is inside an area.
  271. Bool PointInsideArea2D( const Coord3D *ptToTest, const Coord3D *area, Int numPointsInArea)
  272. {
  273. int numIntersections = 0;
  274. Coord2D pt2D, area2D1, area2D2;
  275. pt2D.x = ptToTest->x;
  276. pt2D.y = ptToTest->y;
  277. for (int i = 0; i < numPointsInArea; ++i) {
  278. area2D1.x = area[i].x;
  279. area2D1.y = area[i].y;
  280. area2D2.x = area[(i + 1) % numPointsInArea].x;
  281. area2D2.y = area[(i + 1) % numPointsInArea].y;
  282. if (IntersectLine2D(&pt2D, &reallyFarPoint, &area2D1, &area2D2)) {
  283. ++numIntersections;
  284. }
  285. }
  286. return (numIntersections % 2 == 1);
  287. }
  288. ///< Checks if a point is inside a perfect rectangle (top left and bottom right)
  289. Bool Coord3DInsideRect2D( const Coord3D *inputPoint, const Coord2D *tl, const Coord2D *br )
  290. {
  291. if( inputPoint->x >= tl->x && inputPoint->x <= br->x )
  292. {
  293. if( inputPoint->y >= tl->y && inputPoint->y <= br->y )
  294. {
  295. return TRUE;
  296. }
  297. }
  298. return FALSE;
  299. }
  300. ///< Scales a rect by a factor either growing or shrinking it.
  301. void ScaleRect2D( Coord2D *tl, Coord2D *br, Real scaleFactor )
  302. {
  303. scaleFactor = scaleFactor-1.0f; // We are starting with tl,br, so scaling it by 1 means adding 0 to it.
  304. Real deltaWidth = (br->x - tl->x) * scaleFactor * 0.5f;
  305. Real deltaHeight = (br->y - tl->y) * scaleFactor * 0.5f;
  306. tl->x -= deltaWidth;
  307. tl->y -= deltaHeight;
  308. br->x += deltaWidth;
  309. br->y += deltaHeight;
  310. }
  311. // Solution taken from http://astronomy.swin.edu.au/~pbourke/geometry/pointline/
  312. void ShortestDistancePointToSegment2D( const Coord2D *a, const Coord2D *b, const Coord2D *pt,
  313. Real *outDistance, Coord2D *outPosition, Real *outU )
  314. {
  315. if (!a || !b || !pt) {
  316. return;
  317. }
  318. if (a->x == b->x && a->y == b->y) {
  319. // special case, its simply pt to pt.
  320. Coord2D segment;
  321. segment.x = pt->x - a->x;
  322. segment.y = pt->y - a->y;
  323. if (outDistance) {
  324. (*outDistance) = segment.length();
  325. }
  326. if (outPosition) {
  327. (*outPosition).x = a->x;
  328. (*outPosition).y = a->y;
  329. }
  330. if (outU) {
  331. (*outU) = 0.5;
  332. }
  333. return;
  334. }
  335. Coord2D segAB;
  336. segAB.x = b->x - a->x;
  337. segAB.y = b->y - a->y;
  338. // General case
  339. Real u = ((pt->x - a->x) * (b->x - a->x) + (pt->y - a->y) * (b->y - a->y)) /
  340. sqr(segAB.length());
  341. Coord2D intersectSegment;
  342. intersectSegment.x = a->x + u * (b->x - a->x);
  343. intersectSegment.y = a->y + u * (b->y - a->y);
  344. if (outPosition) {
  345. (*outPosition) = intersectSegment;
  346. }
  347. if (outDistance) {
  348. (*outDistance) = intersectSegment.length();
  349. }
  350. if (outU) {
  351. (*outU) = u;
  352. }
  353. }