Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

916 lines
25 KiB

  1. /******************************Module*Header*******************************\
  2. * Module Name: node.cxx
  3. *
  4. * Pipes node array
  5. *
  6. * Copyright (c) 1995 Microsoft Corporation
  7. *
  8. \**************************************************************************/
  9. #include <stdio.h>
  10. #include <string.h>
  11. #include <stdlib.h>
  12. #include <math.h>
  13. #include <sys/types.h>
  14. #include <time.h>
  15. #include <windows.h>
  16. #include "sspipes.h"
  17. #include "node.h"
  18. /**************************************************************************\
  19. *
  20. * NODE_ARRAY constructor
  21. *
  22. \**************************************************************************/
  23. NODE_ARRAY::NODE_ARRAY()
  24. {
  25. nodes = NULL; // allocated on Resize
  26. numNodes.x = 0;
  27. numNodes.y = 0;
  28. numNodes.z = 0;
  29. }
  30. /**************************************************************************\
  31. *
  32. * NODE_ARRAY destructor
  33. *
  34. \**************************************************************************/
  35. NODE_ARRAY::~NODE_ARRAY( )
  36. {
  37. if( nodes )
  38. delete nodes;
  39. }
  40. /**************************************************************************\
  41. *
  42. * Resize
  43. *
  44. \**************************************************************************/
  45. void
  46. NODE_ARRAY::Resize( IPOINT3D *pNewSize )
  47. {
  48. if( (numNodes.x == pNewSize->x) &&
  49. (numNodes.y == pNewSize->y) &&
  50. (numNodes.z == pNewSize->z) )
  51. return;
  52. numNodes = *pNewSize;
  53. int elemCount = numNodes.x * numNodes.y * numNodes.z ;
  54. if( nodes )
  55. delete nodes;
  56. nodes = new Node[elemCount];
  57. SS_ASSERT( nodes, "NODE_ARRAY::Resize : can't alloc nodes\n" );
  58. // Reset the node states to empty
  59. int i;
  60. Node *pNode = nodes;
  61. for( i = 0; i < elemCount; i++, pNode++ )
  62. pNode->MarkAsEmpty();
  63. // precalculate direction offsets between nodes for speed
  64. nodeDirInc[PLUS_X] = 1;
  65. nodeDirInc[MINUS_X] = -1;
  66. nodeDirInc[PLUS_Y] = numNodes.x;
  67. nodeDirInc[MINUS_Y] = - nodeDirInc[PLUS_Y];
  68. nodeDirInc[PLUS_Z] = numNodes.x * numNodes.y;
  69. nodeDirInc[MINUS_Z] = - nodeDirInc[PLUS_Z];
  70. }
  71. /**************************************************************************\
  72. *
  73. * Reset
  74. *
  75. \**************************************************************************/
  76. void
  77. NODE_ARRAY::Reset( )
  78. {
  79. int i;
  80. Node *pNode = nodes;
  81. // Reset the node states to empty
  82. for( i = 0; i < (numNodes.x)*(numNodes.y)*(numNodes.z); i++, pNode++ )
  83. pNode->MarkAsEmpty();
  84. }
  85. /**************************************************************************\
  86. *
  87. * GetNodeCount
  88. *
  89. \**************************************************************************/
  90. void
  91. NODE_ARRAY::GetNodeCount( IPOINT3D *count )
  92. {
  93. *count = numNodes;
  94. }
  95. /**************************************************************************\
  96. *
  97. * ChooseRandomDirection
  98. *
  99. * Choose randomnly among the possible directions. The likelyhood of going
  100. * straight is controlled by weighting it.
  101. *
  102. \**************************************************************************/
  103. int
  104. NODE_ARRAY::ChooseRandomDirection( IPOINT3D *pos, int dir, int weightStraight )
  105. {
  106. Node *nNode[NUM_DIRS];
  107. int numEmpty, newDir;
  108. int choice;
  109. Node *straightNode = NULL;
  110. int emptyDirs[NUM_DIRS];
  111. SS_ASSERT( (dir >= 0) && (dir < NUM_DIRS),
  112. "NODE_ARRAY::ChooseRandomDirection: invalid dir\n" );
  113. // Get the neigbouring nodes
  114. GetNeighbours( pos, nNode );
  115. // Get node in straight direction if necessary
  116. if( weightStraight && nNode[dir] && nNode[dir]->IsEmpty() ) {
  117. straightNode = nNode[dir];
  118. // if maximum weight, choose and return
  119. if( weightStraight == MAX_WEIGHT_STRAIGHT ) {
  120. straightNode->MarkAsTaken();
  121. return dir;
  122. }
  123. } else
  124. weightStraight = 0;
  125. // Get directions of possible turns
  126. numEmpty = GetEmptyTurnNeighbours( nNode, emptyDirs, dir );
  127. // Make a random choice
  128. if( (choice = (weightStraight + numEmpty)) == 0 )
  129. return DIR_NONE;
  130. choice = ss_iRand( choice );
  131. if( choice < weightStraight ) {
  132. straightNode->MarkAsTaken();
  133. return dir;
  134. } else {
  135. // choose one of the turns
  136. newDir = emptyDirs[choice - weightStraight];
  137. nNode[newDir]->MarkAsTaken();
  138. return newDir;
  139. }
  140. }
  141. /**************************************************************************\
  142. *
  143. * ChoosePreferredDirection
  144. *
  145. * Choose randomnly from one of the supplied preferred directions. If none
  146. * of these are available, then try and choose any empty direction
  147. *
  148. \**************************************************************************/
  149. int
  150. NODE_ARRAY::ChoosePreferredDirection( IPOINT3D *pos, int dir, int *prefDirs,
  151. int nPrefDirs )
  152. {
  153. Node *nNode[NUM_DIRS];
  154. int numEmpty, newDir;
  155. int emptyDirs[NUM_DIRS];
  156. int *pEmptyPrefDirs;
  157. int i, j;
  158. SS_ASSERT( (dir >= 0) && (dir < NUM_DIRS),
  159. "NODE_ARRAY::ChoosePreferredDirection : invalid dir\n" );
  160. // Get the neigbouring nodes
  161. GetNeighbours( pos, nNode );
  162. // Create list of directions that are both preferred and empty
  163. pEmptyPrefDirs = emptyDirs;
  164. numEmpty = 0;
  165. for( i = 0, j = 0; (i < NUM_DIRS) && (j < nPrefDirs); i++ ) {
  166. if( i == *prefDirs ) {
  167. prefDirs++;
  168. j++;
  169. if( nNode[i] && nNode[i]->IsEmpty() ) {
  170. // add it to list
  171. *pEmptyPrefDirs++ = i;
  172. numEmpty++;
  173. }
  174. }
  175. }
  176. // if no empty preferred dirs, then any empty dirs become preferred
  177. if( !numEmpty ) {
  178. numEmpty = GetEmptyNeighbours( nNode, emptyDirs );
  179. if( numEmpty == 0 )
  180. return DIR_NONE;
  181. }
  182. // Pick a random dir from the empty set
  183. newDir = emptyDirs[ss_iRand( numEmpty )];
  184. nNode[newDir]->MarkAsTaken();
  185. return newDir;
  186. }
  187. /**************************************************************************\
  188. *
  189. * FindClearestDirection
  190. *
  191. * Finds the direction with the most empty nodes in a line 'searchRadius'
  192. * long. Does not mark any nodes as taken.
  193. *
  194. \**************************************************************************/
  195. int
  196. NODE_ARRAY::FindClearestDirection( IPOINT3D *pos )
  197. {
  198. static Node *neighbNode[NUM_DIRS];
  199. static int emptyDirs[NUM_DIRS];
  200. int nEmpty, newDir;
  201. int maxEmpty = 0;
  202. int searchRadius = 3;
  203. int count = 0;
  204. int i;
  205. // Get ptrs to neighbour nodes
  206. GetNeighbours( pos, neighbNode );
  207. // find empty nodes in each direction
  208. for( i = 0; i < NUM_DIRS; i ++ ) {
  209. if( neighbNode[i] && neighbNode[i]->IsEmpty() )
  210. {
  211. // find number of contiguous empty nodes along this direction
  212. nEmpty = GetEmptyNeighboursAlongDir( pos, i, searchRadius );
  213. if( nEmpty > maxEmpty ) {
  214. // we have a new winner
  215. count = 0;
  216. maxEmpty = nEmpty;
  217. emptyDirs[count++] = i;
  218. }
  219. else if( nEmpty == maxEmpty ) {
  220. // tied with current max
  221. emptyDirs[count++] = i;
  222. }
  223. }
  224. }
  225. if( count == 0 )
  226. return DIR_NONE;
  227. // randomnly choose a direction
  228. newDir = emptyDirs[ss_iRand( count )];
  229. return newDir;
  230. }
  231. /**************************************************************************\
  232. *
  233. * ChooseNewTurnDirection
  234. *
  235. * Choose a direction to turn
  236. *
  237. * This requires finding a pair of nodes to turn through. The first node
  238. * is in the direction of the turn from the current node, and the second node
  239. * is at right angles to this at the end position. The prim will not draw
  240. * through the first node, but may sweep close to it, so we have to mark it
  241. * as taken.
  242. *
  243. * - if next node is free, but there are no turns available, return
  244. * DIR_STRAIGHT, so the caller can decide what to do in this case
  245. * - The turn possibilities are based on the orientation of the current xc, with
  246. * 4 relative directions to seek turns in.
  247. *
  248. * History
  249. * Aug. 3, 95 : Marc Fortier [marcfo]
  250. * - Wrote it
  251. *
  252. \**************************************************************************/
  253. int
  254. NODE_ARRAY::ChooseNewTurnDirection( IPOINT3D *pos, int dir )
  255. {
  256. Node *nNode[NUM_DIRS];
  257. int turns[NUM_DIRS], nTurns;
  258. IPOINT3D nextPos;
  259. int numEmpty, newDir;
  260. Node *nextNode;
  261. SS_ASSERT( (dir >= 0) && (dir < NUM_DIRS),
  262. "NODE_ARRAY::ChooseNewTurnDirection : invalid dir\n" );
  263. // First, check if next node along current dir is empty
  264. if( ! GetNextNodePos( pos, &nextPos, dir ) )
  265. return DIR_NONE; // node out of bounds or not empty
  266. // Ok, the next node is free - check the 4 possible turns from here
  267. nTurns = GetBestPossibleTurns( &nextPos, dir, turns );
  268. if( nTurns == 0 )
  269. return DIR_STRAIGHT; // nowhere to turn, but could go straight
  270. // randomnly choose one of the possible turns
  271. newDir = turns[ ss_iRand( nTurns ) ];
  272. SS_ASSERT( (newDir >= 0) && (newDir < NUM_DIRS),
  273. "NODE_ARRAY::ChooseNewTurnDirection : invalid newDir\n" );
  274. // mark taken nodes
  275. nextNode = GetNode( &nextPos );
  276. nextNode->MarkAsTaken();
  277. nextNode = GetNextNode( &nextPos, newDir );
  278. nextNode->MarkAsTaken();
  279. return newDir;
  280. }
  281. /**************************************************************************\
  282. *
  283. * GetBestPossibleTurns
  284. *
  285. * From supplied direction and position, figure out which of 4 possible
  286. * directions are best to turn in.
  287. *
  288. * Turns that have the greatest number of empty nodes after the turn are the
  289. * best, since a pipe is less likely to hit a dead end in this case.
  290. * - We only check as far as 'searchRadius' nodes along each dir.
  291. * - Return direction indices of best possible turns in turnDirs, and return
  292. * count of these turns in fuction return value.
  293. *
  294. * History
  295. * Aug. 7, 95 : Marc Fortier [marcfo]
  296. * - Wrote it
  297. *
  298. \**************************************************************************/
  299. int
  300. NODE_ARRAY::GetBestPossibleTurns( IPOINT3D *pos, int dir, int *turnDirs )
  301. {
  302. Node *neighbNode[NUM_DIRS]; // ptrs to 6 neighbour nodes
  303. int i, count = 0;
  304. BOOL check[NUM_DIRS] = {TRUE, TRUE, TRUE, TRUE, TRUE, TRUE};
  305. int nEmpty, maxEmpty = 0;
  306. int searchRadius = 2;
  307. SS_ASSERT( (dir >= 0) && (dir < NUM_DIRS),
  308. "NODE_ARRAY::GetBestPossibleTurns : invalid dir\n" );
  309. GetNeighbours( pos, neighbNode );
  310. switch( dir ) {
  311. case PLUS_X:
  312. case MINUS_X:
  313. check[PLUS_X] = FALSE;
  314. check[MINUS_X] = FALSE;
  315. break;
  316. case PLUS_Y:
  317. case MINUS_Y:
  318. check[PLUS_Y] = FALSE;
  319. check[MINUS_Y] = FALSE;
  320. break;
  321. case PLUS_Z:
  322. case MINUS_Z:
  323. check[PLUS_Z] = FALSE;
  324. check[MINUS_Z] = FALSE;
  325. break;
  326. }
  327. // check approppriate directions
  328. for( i = 0; i < NUM_DIRS; i ++ ) {
  329. if( check[i] && neighbNode[i] && neighbNode[i]->IsEmpty() )
  330. {
  331. // find number of contiguous empty nodes along this direction
  332. nEmpty = GetEmptyNeighboursAlongDir( pos, i, searchRadius );
  333. if( nEmpty > maxEmpty ) {
  334. // we have a new winner
  335. count = 0;
  336. maxEmpty = nEmpty;
  337. turnDirs[count++] = i;
  338. }
  339. else if( nEmpty == maxEmpty ) {
  340. // tied with current max
  341. turnDirs[count++] = i;
  342. }
  343. }
  344. }
  345. return count;
  346. }
  347. /**************************************************************************\
  348. *
  349. * GetNeighbours
  350. *
  351. * Get neigbour nodes relative to supplied position
  352. *
  353. * - get addresses of the neigbour nodes,
  354. * and put them in supplied matrix
  355. * - boundary hits are returned as NULL
  356. *
  357. \**************************************************************************/
  358. void
  359. NODE_ARRAY::GetNeighbours( IPOINT3D *pos, Node **nNode )
  360. {
  361. Node *centerNode = GetNode( pos );
  362. nNode[PLUS_X] = pos->x == (numNodes.x - 1) ? NULL :
  363. centerNode + nodeDirInc[PLUS_X];
  364. nNode[PLUS_Y] = pos->y == (numNodes.y - 1) ? NULL :
  365. centerNode + nodeDirInc[PLUS_Y];
  366. nNode[PLUS_Z] = pos->z == (numNodes.z - 1) ? NULL :
  367. centerNode + nodeDirInc[PLUS_Z];
  368. nNode[MINUS_X] = pos->x == 0 ? NULL : centerNode + nodeDirInc[MINUS_X];
  369. nNode[MINUS_Y] = pos->y == 0 ? NULL : centerNode + nodeDirInc[MINUS_Y];
  370. nNode[MINUS_Z] = pos->z == 0 ? NULL : centerNode + nodeDirInc[MINUS_Z];
  371. }
  372. /**************************************************************************\
  373. *
  374. * NodeVisited
  375. *
  376. * Mark the node as non-empty
  377. *
  378. \**************************************************************************/
  379. void
  380. NODE_ARRAY::NodeVisited( IPOINT3D *pos )
  381. {
  382. (GetNode( pos ))->MarkAsTaken();
  383. }
  384. /**************************************************************************\
  385. *
  386. * GetNode
  387. *
  388. * Get ptr to node from position
  389. *
  390. \**************************************************************************/
  391. Node *
  392. NODE_ARRAY::GetNode( IPOINT3D *pos )
  393. {
  394. return nodes +
  395. pos->x +
  396. pos->y * numNodes.x +
  397. pos->z * numNodes.x * numNodes.y;
  398. }
  399. /**************************************************************************\
  400. *
  401. * GetNextNode
  402. *
  403. * Get ptr to next node from pos and dir
  404. *
  405. \**************************************************************************/
  406. Node *
  407. NODE_ARRAY::GetNextNode( IPOINT3D *pos, int dir )
  408. {
  409. Node *curNode = GetNode( pos );
  410. SS_ASSERT( (dir >= 0) && (dir < NUM_DIRS),
  411. "NODE_ARRAY::GetNextNode : invalid dir\n" );
  412. switch( dir ) {
  413. case PLUS_X:
  414. return( pos->x == (numNodes.x - 1) ? NULL :
  415. curNode + nodeDirInc[PLUS_X]);
  416. break;
  417. case MINUS_X:
  418. return( pos->x == 0 ? NULL :
  419. curNode + nodeDirInc[MINUS_X]);
  420. break;
  421. case PLUS_Y:
  422. return( pos->y == (numNodes.y - 1) ? NULL :
  423. curNode + nodeDirInc[PLUS_Y]);
  424. break;
  425. case MINUS_Y:
  426. return( pos->y == 0 ? NULL :
  427. curNode + nodeDirInc[MINUS_Y]);
  428. break;
  429. case PLUS_Z:
  430. return( pos->z == (numNodes.z - 1) ? NULL :
  431. curNode + nodeDirInc[PLUS_Z]);
  432. break;
  433. case MINUS_Z:
  434. return( pos->z == 0 ? NULL :
  435. curNode + nodeDirInc[MINUS_Z]);
  436. break;
  437. default:
  438. return NULL;
  439. }
  440. }
  441. /**************************************************************************\
  442. *
  443. * GetNextNodePos
  444. *
  445. * Get position of next node from curPos and lastDir
  446. *
  447. * Returns FALSE if boundary hit or node empty
  448. *
  449. \**************************************************************************/
  450. BOOL
  451. NODE_ARRAY::GetNextNodePos( IPOINT3D *curPos, IPOINT3D *nextPos, int dir )
  452. {
  453. static Node *neighbNode[NUM_DIRS]; // ptrs to 6 neighbour nodes
  454. SS_ASSERT( (dir >= 0) && (dir < NUM_DIRS),
  455. "NODE_ARRAY::GetNextNodePos : invalid dir\n" );
  456. //mf: don't need to get all neighbours, just one in next direction
  457. GetNeighbours( curPos, neighbNode );
  458. *nextPos = *curPos;
  459. // bail if boundary hit or node not empty
  460. if( (neighbNode[dir] == NULL) || !neighbNode[dir]->IsEmpty() )
  461. return FALSE;
  462. switch( dir ) {
  463. case PLUS_X:
  464. nextPos->x = curPos->x + 1;
  465. break;
  466. case MINUS_X:
  467. nextPos->x = curPos->x - 1;
  468. break;
  469. case PLUS_Y:
  470. nextPos->y = curPos->y + 1;
  471. break;
  472. case MINUS_Y:
  473. nextPos->y = curPos->y - 1;
  474. break;
  475. case PLUS_Z:
  476. nextPos->z = curPos->z + 1;
  477. break;
  478. case MINUS_Z:
  479. nextPos->z = curPos->z - 1;
  480. break;
  481. }
  482. return TRUE;
  483. }
  484. /**************************************************************************\
  485. *
  486. * GetEmptyNeighbours()
  487. * - get list of direction indices of empty node neighbours,
  488. * and put them in supplied matrix
  489. * - return number of empty node neighbours
  490. *
  491. \**************************************************************************/
  492. int
  493. NODE_ARRAY::GetEmptyNeighbours( Node **nNode, int *nEmpty )
  494. {
  495. int i, count = 0;
  496. for( i = 0; i < NUM_DIRS; i ++ ) {
  497. if( nNode[i] && nNode[i]->IsEmpty() )
  498. nEmpty[count++] = i;
  499. }
  500. return count;
  501. }
  502. /**************************************************************************\
  503. *
  504. * GetEmptyTurnNeighbours()
  505. * - get list of direction indices of empty node neighbours,
  506. * and put them in supplied matrix
  507. * - don't include going straight
  508. * - return number of empty node neighbours
  509. *
  510. \**************************************************************************/
  511. int
  512. NODE_ARRAY::GetEmptyTurnNeighbours( Node **nNode, int *nEmpty, int lastDir )
  513. {
  514. int i, count = 0;
  515. for( i = 0; i < NUM_DIRS; i ++ ) {
  516. if( nNode[i] && nNode[i]->IsEmpty() ) {
  517. if( i == lastDir )
  518. continue;
  519. nEmpty[count++] = i;
  520. }
  521. }
  522. return count;
  523. }
  524. /**************************************************************************\
  525. * GetEmptyNeighboursAlongDir
  526. *
  527. * Sort of like above, but just gets one neigbour according to supplied dir
  528. *
  529. * Given a position and direction, find out how many contiguous empty nodes
  530. * there are in that direction.
  531. * - Can limit search with searchRadius parameter
  532. * - Return contiguous empty node count
  533. *
  534. * History
  535. * Aug. 12, 95 : [marcfo]
  536. * - Wrote it
  537. *
  538. \**************************************************************************/
  539. int
  540. NODE_ARRAY::GetEmptyNeighboursAlongDir( IPOINT3D *pos, int dir,
  541. int searchRadius )
  542. {
  543. Node *curNode = GetNode( pos );
  544. int nodeStride;
  545. int maxSearch;
  546. int count = 0;
  547. SS_ASSERT( (dir >= 0) && (dir < NUM_DIRS),
  548. "NODE_ARRAY::GetEmptyNeighboursAlongDir : invalid dir\n" );
  549. nodeStride = nodeDirInc[dir];
  550. switch( dir ) {
  551. case PLUS_X:
  552. maxSearch = numNodes.x - pos->x - 1;
  553. break;
  554. case MINUS_X:
  555. maxSearch = pos->x;
  556. break;
  557. case PLUS_Y:
  558. maxSearch = numNodes.y - pos->y - 1;
  559. break;
  560. case MINUS_Y:
  561. maxSearch = pos->y;
  562. break;
  563. case PLUS_Z:
  564. maxSearch = numNodes.z - pos->z - 1;
  565. break;
  566. case MINUS_Z:
  567. maxSearch = pos->z;
  568. break;
  569. }
  570. if( searchRadius > maxSearch )
  571. searchRadius = maxSearch;
  572. if( !searchRadius )
  573. return 0;
  574. while( searchRadius-- ) {
  575. curNode += nodeStride;
  576. if( ! curNode->IsEmpty() )
  577. return count;
  578. count++;
  579. }
  580. return count;
  581. }
  582. /**************************************************************************\
  583. * FindRandomEmptyNode
  584. *
  585. * - Search for an empty node to start drawing
  586. * - Return position of empty node in supplied pos ptr
  587. * - Returns FALSE if couldn't find a node
  588. * - Marks node as taken (mf: renam fn to ChooseEmptyNode ?
  589. *
  590. * History
  591. * July 19, 95 : [marcfo]
  592. * - Wrote it
  593. *
  594. \**************************************************************************/
  595. // If random search takes longer than twice the total number
  596. // of nodes, give up the random search. There may not be any
  597. // empty nodes.
  598. #define INFINITE_LOOP (2 * NUM_NODE * NUM_NODE * NUM_NODE)
  599. BOOL
  600. NODE_ARRAY::FindRandomEmptyNode( IPOINT3D *pos )
  601. {
  602. int infLoopDetect = 0;
  603. while( TRUE ) {
  604. // Pick a random node.
  605. pos->x = ss_iRand( numNodes.x );
  606. pos->y = ss_iRand( numNodes.y );
  607. pos->z = ss_iRand( numNodes.z );
  608. // If its empty, we're done.
  609. if( GetNode(pos)->IsEmpty() ) {
  610. NodeVisited( pos );
  611. return TRUE;
  612. } else {
  613. // Watch out for infinite loops! After trying for
  614. // awhile, give up on the random search and look
  615. // for the first empty node.
  616. if ( infLoopDetect++ > INFINITE_LOOP ) {
  617. // Search for first empty node.
  618. for ( pos->x = 0; pos->x < numNodes.x; pos->x++ )
  619. for ( pos->y = 0; pos->y < numNodes.y; pos->y++ )
  620. for ( pos->z = 0; pos->z < numNodes.z; pos->z++ )
  621. if( GetNode(pos)->IsEmpty() ) {
  622. NodeVisited( pos );
  623. return TRUE;
  624. }
  625. // There are no more empty nodes.
  626. // Reset the pipes and exit.
  627. return FALSE;
  628. }
  629. }
  630. }
  631. }
  632. /**************************************************************************\
  633. * FindRandomEmptyNode2D
  634. *
  635. * - Like FindRandomEmptyNode, but limits search to a 2d plane of the supplied
  636. * box.
  637. *
  638. \**************************************************************************/
  639. #define INFINITE_LOOP (2 * NUM_NODE * NUM_NODE * NUM_NODE)
  640. #define MIN_VAL 1
  641. #define MAX_VAL 0
  642. BOOL
  643. NODE_ARRAY::FindRandomEmptyNode2D( IPOINT3D *pos, int plane, int *box )
  644. {
  645. int *newx, *newy;
  646. int *xDim, *yDim;
  647. switch( plane ) {
  648. case PLUS_X:
  649. case MINUS_X:
  650. pos->x = box[plane];
  651. newx = &pos->z;
  652. newy = &pos->y;
  653. xDim = &box[PLUS_Z];
  654. yDim = &box[PLUS_Y];
  655. break;
  656. case PLUS_Y:
  657. case MINUS_Y:
  658. pos->y = box[plane];
  659. newx = &pos->x;
  660. newy = &pos->z;
  661. xDim = &box[PLUS_X];
  662. yDim = &box[PLUS_Z];
  663. break;
  664. case PLUS_Z:
  665. case MINUS_Z:
  666. newx = &pos->x;
  667. newy = &pos->y;
  668. pos->z = box[plane];
  669. xDim = &box[PLUS_X];
  670. yDim = &box[PLUS_Y];
  671. break;
  672. }
  673. int infLoop = 2 * (xDim[MAX_VAL] - xDim[MIN_VAL] + 1) *
  674. (yDim[MAX_VAL] - yDim[MIN_VAL] + 1);
  675. int infLoopDetect = 0;
  676. while( TRUE ) {
  677. // Pick a random node.
  678. *newx = ss_iRand2( xDim[MIN_VAL], xDim[MAX_VAL] );
  679. *newy = ss_iRand2( yDim[MIN_VAL], yDim[MAX_VAL] );
  680. // If its empty, we're done.
  681. if( GetNode(pos)->IsEmpty() ) {
  682. NodeVisited( pos );
  683. return TRUE;
  684. } else {
  685. // Watch out for infinite loops! After trying for
  686. // awhile, give up on the random search and look
  687. // for the first empty node.
  688. if ( ++infLoopDetect > infLoop ) {
  689. // Do linear search for first empty node.
  690. for ( *newx = xDim[MIN_VAL]; *newx <= xDim[MAX_VAL]; (*newx)++ )
  691. for ( *newy = yDim[MIN_VAL]; *newy <= yDim[MAX_VAL]; (*newy)++ )
  692. if( GetNode(pos)->IsEmpty() ) {
  693. NodeVisited( pos );
  694. return TRUE;
  695. }
  696. // There are no empty nodes in this plane.
  697. return FALSE;
  698. }
  699. }
  700. }
  701. }
  702. /**************************************************************************\
  703. * TakeClosestEmptyNode
  704. *
  705. * - Search for an empty node closest to supplied node position
  706. * - Returns FALSE if couldn't find a node
  707. * - Marks node as taken
  708. * - mf: not completely opimized - if when dilating the box, a side gets
  709. * clamped against the node array, this side will continue to be searched
  710. *
  711. * History
  712. * Dec 95 : [marcfo]
  713. * - Wrote it
  714. *
  715. \**************************************************************************/
  716. static void
  717. DilateBox( int *box, IPOINT3D *bounds );
  718. BOOL
  719. NODE_ARRAY::TakeClosestEmptyNode( IPOINT3D *newPos, IPOINT3D *pos )
  720. {
  721. static int searchRadius = SS_MAX( numNodes.x, numNodes.y ) / 3;
  722. // easy out
  723. if( GetNode(pos)->IsEmpty() ) {
  724. NodeVisited( pos );
  725. *newPos = *pos;
  726. return TRUE;
  727. }
  728. int box[NUM_DIRS] = {pos->x, pos->x, pos->y, pos->y, pos->z, pos->z};
  729. int clip[NUM_DIRS] = {0};
  730. // do a random search on successively larger search boxes
  731. for( int i = 0; i < searchRadius; i++ ) {
  732. // Increase box size
  733. DilateBox( box, &numNodes );
  734. // start looking in random 2D face of the box
  735. int dir = ss_iRand( NUM_DIRS );
  736. for( int j = 0; j < NUM_DIRS; j++, dir = (++dir == NUM_DIRS) ? 0 : dir ) {
  737. if( FindRandomEmptyNode2D( newPos, dir, box ) )
  738. return TRUE;
  739. }
  740. }
  741. // nothing nearby - grab a random one
  742. return FindRandomEmptyNode( newPos );
  743. }
  744. /**************************************************************************\
  745. * DilateBox
  746. *
  747. * - Increase box radius without exceeding bounds
  748. *
  749. \**************************************************************************/
  750. static void
  751. DilateBox( int *box, IPOINT3D *bounds )
  752. {
  753. int *min = (int *) &box[MINUS_X];
  754. int *max = (int *) &box[PLUS_X];
  755. int *boundMax = (int *) bounds;
  756. // boundMin always 0
  757. for( int i = 0; i < 3; i ++, min+=2, max+=2, boundMax++ ) {
  758. if( *min > 0 )
  759. (*min)--;
  760. if( *max < (*boundMax - 1) )
  761. (*max)++;
  762. }
  763. }