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.

897 lines
26 KiB

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