PageRenderTime 60ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/NetduinoLEDControl/Program.cs

#
C# | 1105 lines | 794 code | 76 blank | 235 comment | 130 complexity | 182c56f05c9ef398953e081eb867fd4f MD5 | raw file
  1. //=======================================================================================
  2. //
  3. // Purpose: Main program for controlling a 8 x 8 x 8 LED cube using 74HC595 shift
  4. // registers and 74HC238 3-to-8 line multiplers using the Netduino Mini
  5. // microcontroller.
  6. //
  7. // Copyright (C) 2011 Mark Stevens
  8. //
  9. // This software is destributed under the MS-PL licence agreement a copy of which can
  10. // be found on the codeplex page http://netduinoledcontrol.codeplex.com and in the
  11. // Licence.txt file distributed with this project.
  12. //
  13. //=======================================================================================
  14. using System;
  15. using System.Threading;
  16. using Microsoft.SPOT.Hardware;
  17. using Microsoft.SPOT;
  18. using System.Collections;
  19. namespace Coding4Fun.NetduinoLEDControl
  20. {
  21. /// <summary>
  22. /// Main program for the Netduino cube.
  23. /// </summary>
  24. public class Program
  25. {
  26. #region Enums
  27. /// <summary>
  28. /// Which axis should we rotate around?
  29. /// </summary>
  30. private enum AxisOfRotation
  31. {
  32. /// <summary>
  33. /// Rotate around the X axis.
  34. /// </summary>
  35. X,
  36. /// <summary>
  37. /// Rotate arouns the Y axis.
  38. /// </summary>
  39. Y,
  40. /// <summary>
  41. /// Rotate around the Z axiz.
  42. /// </summary>
  43. Z
  44. };
  45. #endregion
  46. #region Private static variables.
  47. /// <summary>
  48. /// Letters holding the character set.
  49. /// </summary>
  50. private static PlanarBitmap[] characterSet = new PlanarBitmap[29];
  51. /// <summary>
  52. /// Set up and seed the random number generator.
  53. /// </summary>
  54. private static Random rand = new Random((int) (Utility.GetMachineTime().Ticks & 0xffffffff));
  55. /// <summary>
  56. /// Create a new instance of the cube.
  57. /// </summary>
  58. private static LEDCube cube = new LEDCube();
  59. /// <summary>
  60. /// Buffer to be used to hold the working (not displayed) copy of the cube data.
  61. /// </summary>
  62. private static byte[] newFrame = new byte[64];
  63. #endregion
  64. #region Generic support methods.
  65. /// <summary>
  66. /// Set or rest a specific pixel in the cube.
  67. /// </summary>
  68. /// <param name="x">X co-ordinate of the pixel to be set.</param>
  69. /// <param name="y">Y co-ordinate of thge pixel to be set.</param>
  70. /// <param name="z">Plane of the pixel to be set.</param>
  71. /// <param name="value">Turn the pixel on or off: true = turn on, false - turn off</param>
  72. private static void SetPixel(int x, int y, int z, bool value)
  73. {
  74. if ((x >= 0) && (x <= 7) && (y >= 0) && (y <= 7) && (z >= 0) && (z <= 7))
  75. {
  76. SetPixel(newFrame, x, y, z, value);
  77. }
  78. }
  79. /// <summary>
  80. /// Set or rest a specific pixel in the specified buffer.
  81. /// </summary>
  82. /// <param name="buffer">Buffer to set the pixel in.</param>
  83. /// <param name="x">X co-ordinate of the pixel to be set.</param>
  84. /// <param name="y">Y co-ordinate of thge pixel to be set.</param>
  85. /// <param name="z">Plane of the pixel to be set.</param>
  86. /// <param name="value">Turn the pixel on or off: true = turn on, false - turn off</param>
  87. private static void SetPixel(byte[] buffer, int x, int y, int z, bool value)
  88. {
  89. int whichOne = (y * 8) + z;
  90. if (value)
  91. {
  92. buffer[whichOne] |= (byte) ((1 << x) & 0xff);
  93. }
  94. else
  95. {
  96. buffer[whichOne] &= (byte) ((~(1 << x)) & 0xff);
  97. }
  98. }
  99. /// <summary>
  100. /// Get the value of a pixel
  101. /// </summary>
  102. /// <param name="x">X co-ordinate of the pixel to be set.</param>
  103. /// <param name="y">Y co-ordinate of thge pixel to be set.</param>
  104. /// <param name="z">PLane of the pixel to be set.</param>
  105. /// <returns>True if the pixel is turned on, false otherwise.</returns>
  106. private static bool GetPixel(byte[] buffer, int x, int y, int z)
  107. {
  108. int whichOne = (y * 8) + z;
  109. return ((buffer[whichOne] & ((byte) ((1 << x) & 0xff))) != 0);
  110. }
  111. /// <summary>
  112. /// Clear the cube.
  113. /// </summary>
  114. /// <remarks>
  115. /// Clear the cube of data and refresh the cube buffer.
  116. /// </remarks>
  117. private static void ClearCube()
  118. {
  119. ClearBuffer(newFrame);
  120. cube.ClearCube();
  121. }
  122. /// <summary>
  123. /// Clear the specified buffer (i.e. set each byte to zero).
  124. /// </summary>
  125. /// <param name="buffer">Buffer to clear.</param>
  126. private static void ClearBuffer(byte[] buffer)
  127. {
  128. for (int count = 0; count < buffer.Length; count++)
  129. {
  130. buffer[count] = 0;
  131. }
  132. }
  133. /// <summary>
  134. /// Coipy one frame (buffer) into a new frame (buffer);
  135. /// </summary>
  136. /// <param name="source">Source bytes to copy.</param>
  137. /// <param name="destination">Destination location for the copy of the data.</param>
  138. private static void CopyFrame(byte[] source, byte[] destination)
  139. {
  140. for (int count = 0; count < source.Length; count++)
  141. {
  142. destination[count] = source[count];
  143. }
  144. }
  145. #endregion
  146. #region Some test methods.
  147. /// <summary>
  148. /// A quick test to verify that all of the LEDs work correctly.
  149. /// </summary>
  150. private static void Test()
  151. {
  152. ClearCube();
  153. while (true)
  154. {
  155. for (int bit = 0; bit < 8; bit++)
  156. {
  157. byte v;
  158. v = (byte) ((1 << bit) & 0xff);
  159. for (int count = 0; count < 64; count++)
  160. {
  161. newFrame[count] = v;
  162. }
  163. cube.UpdateBuffer(newFrame);
  164. Thread.Sleep(1);
  165. }
  166. }
  167. }
  168. /// <summary>
  169. /// Test all of the LEDs in the cube.
  170. /// </summary>
  171. private static void TestAllLeds()
  172. {
  173. for (int z = 0; z < 8; z++)
  174. {
  175. for (int y = 0; y < 8; y++)
  176. {
  177. for (int x = 0; x < 8; x++)
  178. {
  179. ClearCube();
  180. SetPixel(x, y, z, true);
  181. cube.UpdateBuffer(newFrame);
  182. Thread.Sleep(5);
  183. }
  184. }
  185. }
  186. }
  187. /// <summary>
  188. /// Cycle through each LED in the specified layer.
  189. /// </summary>
  190. /// <param name="layer">Layer to test.</param>
  191. private static void TestLayer(int layer)
  192. {
  193. for (int z = 0; z < 8; z++)
  194. {
  195. for (int x = 0; x < 8; x++)
  196. {
  197. ClearCube();
  198. SetPixel(x, layer, z, true);
  199. cube.UpdateBuffer(newFrame);
  200. Thread.Sleep(5);
  201. }
  202. }
  203. }
  204. #endregion
  205. #region Bouncing planes.
  206. /// <summary>
  207. /// Show planes moving from the bottom of the cube to the top of the cube.
  208. /// </summary>
  209. private static void BouncePlaneBottomToTop(int noCycles)
  210. {
  211. int direction = 1;
  212. int plane = 0;
  213. for (int repeat = 0; repeat < noCycles; repeat++)
  214. {
  215. for (int index = 0; index < 8; index++)
  216. {
  217. ClearCube();
  218. int offset = plane * 8;
  219. for (int counter = 0; counter < 8; counter++)
  220. {
  221. newFrame[offset + counter] = 0xff;
  222. }
  223. plane += direction;
  224. cube.UpdateBuffer(newFrame);
  225. Thread.Sleep(100);
  226. }
  227. direction *= -1;
  228. if (plane == 8)
  229. {
  230. plane = 7;
  231. }
  232. if (plane < 0)
  233. {
  234. plane = 0;
  235. }
  236. }
  237. }
  238. /// <summary>
  239. /// Move a plane from the back of the cube to the front of the cube.
  240. /// </summary>
  241. private static void BouncePlaneLeftToRight(int noCycles)
  242. {
  243. for (int repeat = 0; repeat < noCycles; repeat++)
  244. {
  245. byte value = 1;
  246. for (int index = 0; index < 8; index++)
  247. {
  248. for (int counter = 0; counter < newFrame.Length; counter++)
  249. {
  250. newFrame[counter] = value;
  251. }
  252. cube.UpdateBuffer(newFrame);
  253. value <<= 1;
  254. Thread.Sleep(100);
  255. }
  256. value = 128;
  257. for (int index = 7; index >= 0; index--)
  258. {
  259. for (int counter = 0; counter < newFrame.Length; counter++)
  260. {
  261. newFrame[counter] = value;
  262. }
  263. cube.UpdateBuffer(newFrame);
  264. value >>= 1;
  265. Thread.Sleep(100);
  266. }
  267. }
  268. }
  269. /// <summary>
  270. /// Show a plane moving from the back to the front of the cube.
  271. /// </summary>
  272. private static void BouncePlaneBackToFront(int noCycles)
  273. {
  274. int direction = 1;
  275. int plane = 0;
  276. for (int repeat = 0; repeat < noCycles; repeat++)
  277. {
  278. byte value = 1;
  279. for (int index = 0; index < 8; index++)
  280. {
  281. ClearCube();
  282. for (int counter = 0; counter < 8; counter++)
  283. {
  284. newFrame[(counter * 8) + plane] = 0xff;
  285. }
  286. cube.UpdateBuffer(newFrame);
  287. plane += direction;
  288. value <<= 1;
  289. Thread.Sleep(100);
  290. }
  291. direction *= -1;
  292. if (plane == 8)
  293. {
  294. plane = 7;
  295. }
  296. if (plane < 0)
  297. {
  298. plane = 0;
  299. }
  300. }
  301. }
  302. #endregion
  303. #region Showing a message.
  304. /// <summary>
  305. /// Get the dot pattern with the specified name.
  306. /// </summary>
  307. /// <param name="name">Name of the bitmap to look up.</param>
  308. /// <returns>Array of bytes containing the dot patern for the character.</returns>
  309. private static byte[] LookupPattern(string name)
  310. {
  311. foreach (PlanarBitmap l in characterSet)
  312. {
  313. if (l.Name == name)
  314. {
  315. return (l.DotPattern);
  316. }
  317. }
  318. return (null);
  319. }
  320. /// <summary>
  321. /// Show the string from the back to the front of the cube.
  322. /// </summary>
  323. /// <param name="message">Message to display.</param>
  324. private static void ShowMessage(string message)
  325. {
  326. for (int index = 0; index < message.Length; index++)
  327. {
  328. byte[] dotPattern = LookupPattern(message[index].ToString());
  329. for (int plane = 0; plane < 8; plane++)
  330. {
  331. ClearCube();
  332. if (dotPattern != null)
  333. {
  334. for (int row = 0; row < 8; row++)
  335. {
  336. newFrame[(row * 8) + plane] = dotPattern[row];
  337. }
  338. cube.UpdateBuffer(newFrame);
  339. }
  340. Thread.Sleep(50);
  341. }
  342. ClearCube();
  343. }
  344. }
  345. #endregion
  346. #region Rain effect methods.
  347. /// <summary>
  348. /// Add rain drops to the cube.
  349. /// </summary>
  350. /// <remarks>
  351. /// The algorithm will always add the specified number of rain drops to the cube.
  352. /// If the selected position is already occupied then a new psoition will be
  353. /// selected.
  354. /// </remarks>
  355. /// <param name="count">Number of rain drops to add.</param>
  356. /// <param name="plane">
  357. /// Starting plane. -1 indicates the plane should be random otherwise the
  358. /// specified starting plane will be used.
  359. /// </param>
  360. private static void AddDrops(int count, int plane = -1)
  361. {
  362. for (int drops = 0; drops < count; drops++)
  363. {
  364. bool findingSpace = true;
  365. while (findingSpace)
  366. {
  367. int x = rand.Next() % 8;
  368. int y = rand.Next() % 8;
  369. int z;
  370. if (plane == -1)
  371. {
  372. z = rand.Next() % 8;
  373. }
  374. else
  375. {
  376. z = plane;
  377. }
  378. int position = (z * 8) + y;
  379. byte value = (byte) ((1 << x) & 0xff);
  380. if ((newFrame[position] & value) == 0) // Check if there is already a rain drop in this position.
  381. {
  382. newFrame[position] |= value;
  383. findingSpace = false;
  384. }
  385. }
  386. }
  387. }
  388. /// <summary>
  389. /// Produce an effect of rain falling through the cube.
  390. /// </summary>
  391. /// <remarks>
  392. /// The initial position of the drops is random New drops will always be
  393. /// added in to the top plane.
  394. ///
  395. /// The number of drops leaving the cube will be calculated and the same
  396. /// number of drops will always be added in at the top of the cube.
  397. /// </remarks>
  398. /// <param name="noDrops">Number of drops to start with.</param>
  399. /// <param name="cycles">
  400. /// Number of cycles to run for. One cycle represnes the movement of one
  401. /// drop down through the cube by one layer.
  402. /// </param>
  403. private static void Rain(int noDrops, int cycles)
  404. {
  405. ClearCube();
  406. AddDrops(noDrops);
  407. cube.UpdateBuffer(newFrame);
  408. for (int currentCycle = 0; currentCycle < cycles; currentCycle++)
  409. {
  410. #region Work out how many drops are on the bottom plane.
  411. int bitCount = 0;
  412. for (int verticalPlane = 0; verticalPlane < 8; verticalPlane++)
  413. {
  414. byte value = 1;
  415. for (int bit = 0; bit < 8; bit++)
  416. {
  417. if ((newFrame[verticalPlane] & value) > 0)
  418. {
  419. bitCount++;
  420. }
  421. value <<= 1;
  422. }
  423. }
  424. #endregion
  425. #region Move the drops down by one plane and clear the top plane.
  426. for (int plane = 8; plane < 64; plane++)
  427. {
  428. newFrame[plane - 8] = newFrame[plane];
  429. }
  430. for (int b = 56; b < 64; b++)
  431. {
  432. newFrame[b] = 0;
  433. }
  434. #endregion
  435. AddDrops(bitCount, 7); // Add in the number of drops leaving to the top plane.
  436. cube.UpdateBuffer(newFrame);
  437. Thread.Sleep(75);
  438. }
  439. }
  440. #endregion
  441. #region Wave effects.
  442. /// <summary>
  443. /// Perform a Mexican wave.
  444. /// </summary>
  445. /// <param name="noCycles">Number of complete waves to perform.</param>
  446. private static void MexicanWave(int noCycles = 10)
  447. {
  448. ClearCube();
  449. for (int z = 0; z < 8; z++)
  450. {
  451. SetPixel(0, 7, z, true);
  452. }
  453. for (int cycle = 0; cycle < noCycles; cycle++)
  454. {
  455. for (int height = 7; height >= 0; height--)
  456. {
  457. for (int bytes = 0; bytes < 64; bytes++)
  458. {
  459. newFrame[bytes] <<= 1;
  460. }
  461. for (int z = 0; z < 8; z++)
  462. {
  463. SetPixel(0, height, z, true);
  464. }
  465. cube.UpdateBuffer(newFrame);
  466. Thread.Sleep(100);
  467. }
  468. for (int height = 7; height >= 0; height--)
  469. {
  470. for (int bytes = 0; bytes < 64; bytes++)
  471. {
  472. newFrame[bytes] <<= 1;
  473. }
  474. for (int z = 0; z < 8; z++)
  475. {
  476. SetPixel(0, 7 - height, z, true);
  477. }
  478. cube.UpdateBuffer(newFrame);
  479. Thread.Sleep(100);
  480. }
  481. }
  482. }
  483. /// <summary>
  484. /// Show a triangular wave.
  485. /// </summary>
  486. /// <param name="noCycles">Number of complete waves to perform.</param>
  487. private static void TriangularWave(int noCycles = 10)
  488. {
  489. ClearCube();
  490. for (int cycle = 0; cycle < noCycles; cycle++)
  491. {
  492. for (int height = 7; height >= 0; height--)
  493. {
  494. for (int bytes = 0; bytes < 64; bytes++)
  495. {
  496. newFrame[bytes] <<= 1;
  497. }
  498. for (int z = 0; z < 8; z++)
  499. {
  500. SetPixel(z, height, z, true);
  501. }
  502. cube.UpdateBuffer(newFrame);
  503. Thread.Sleep(100);
  504. }
  505. for (int height = 7; height >= 0; height--)
  506. {
  507. for (int bytes = 0; bytes < 64; bytes++)
  508. {
  509. newFrame[bytes] <<= 1;
  510. }
  511. for (int z = 0; z < 8; z++)
  512. {
  513. SetPixel(z, 7 - height, z, true);
  514. }
  515. cube.UpdateBuffer(newFrame);
  516. Thread.Sleep(100);
  517. }
  518. }
  519. }
  520. #endregion
  521. #region Rotation Effects.
  522. /// <summary>
  523. /// Rotate the source buffer around a particular point in a plane.
  524. /// </summary>
  525. /// <param name="axis">Axis to rotate arouns.</param>
  526. /// <param name="xOffset">X offset of the origin.</param>
  527. /// <param name="yOffset">Y offset of the origin.</param>
  528. /// <param name="zOffset">Z offset of the origin.</param>
  529. /// <param name="theta">angle to roate through.</param>
  530. /// <param name="source">Buffer holding the original data.</param>
  531. /// <param name="destination">New buffer holding the rotated pixels.</param>
  532. private static void RotateAroundAxis(AxisOfRotation axis, float xOffset, float yOffset, float zOffset, int theta, byte[] source, byte[] destination)
  533. {
  534. ClearBuffer(destination);
  535. float cosTheta = ((float) Microsoft.SPOT.Math.Cos(theta)) / 1000;
  536. float sinTheta = ((float) Microsoft.SPOT.Math.Sin(theta)) / 1000;
  537. for (int x = 0; x < 8; x++)
  538. {
  539. for (int y = 0; y < 8; y++)
  540. {
  541. for (int z = 0; z < 8; z++)
  542. {
  543. if (GetPixel(source, x, y, z))
  544. {
  545. float xCoord, yCoord, zCoord;
  546. int newX = 0, newY = 0, newZ = 0;
  547. switch (axis)
  548. {
  549. case AxisOfRotation.X:
  550. xCoord = x - xOffset;
  551. yCoord = y - yOffset;
  552. newX = (int) System.Math.Round(((xCoord * cosTheta) - (yCoord * sinTheta)) + xOffset);
  553. newY = (int) System.Math.Round(((xCoord * sinTheta) + (yCoord * cosTheta)) + yOffset);
  554. newZ = z;
  555. break;
  556. case AxisOfRotation.Y:
  557. xCoord = x - xOffset;
  558. zCoord = z - zOffset;
  559. newX = (int) System.Math.Round(((zCoord * sinTheta) + (xCoord * cosTheta)) + xOffset);
  560. newY = y;
  561. newZ = (int) System.Math.Round(((zCoord * cosTheta) - (xCoord * sinTheta)) + zOffset);
  562. break;
  563. case AxisOfRotation.Z:
  564. yCoord = y - yOffset;
  565. zCoord = z - zOffset;
  566. newX = x;
  567. newY = (int) System.Math.Round(((yCoord * cosTheta) - (zCoord * sinTheta)) + xOffset);
  568. newZ = (int) System.Math.Round(((yCoord * sinTheta) + (zCoord * sinTheta)) + zOffset);
  569. break;
  570. }
  571. if ((newX >= 0) && (newX <= 7) && (newY >= 0) && (newY <= 7) && (newZ >= 0) && (newZ <= 7))
  572. {
  573. SetPixel(destination, newX, newY, newZ, true);
  574. }
  575. }
  576. }
  577. }
  578. }
  579. }
  580. /// <summary>
  581. /// Spin the given buffer through 360 degrees for the specified number of times.
  582. /// </summary>
  583. /// <param name="buffer">Cube buffer to rotate.</param>
  584. /// <param name="x">x Coordinat to spin the buffer around (default = 3.5)</param>
  585. /// <param name="y">y Coordinat to spin the buffer around (default = 3.5)</param>
  586. /// <param name="z">z Coordinat to spin the buffer around (default = 3.5)</param>
  587. /// <returns>16 frame buffer which has been animated.</returns>
  588. private static byte[][] SpinBufferAroundYAxis(byte[] buffer, float x = 3.5F, float y = 3.5F, float z = 3.5F)
  589. {
  590. byte[][] frames = new byte[16][];
  591. frames[0] = new byte[64];
  592. CopyFrame(buffer, frames[0]);
  593. for (int iteration = 1; iteration < 16; iteration++)
  594. {
  595. frames[iteration] = new byte[64];
  596. RotateAroundAxis(AxisOfRotation.Y, x, y, z, (int) (22.5 * iteration), frames[0], frames[iteration]);
  597. }
  598. return (frames);
  599. }
  600. /// <summary>
  601. /// Put the requested letter in the centre of the cube and make it spin.
  602. /// </summary>
  603. /// <remarks>
  604. /// This method spins a character round the centre of the cube. It works but does require
  605. /// the viewer to squint a little to best appreciate the effect.
  606. /// </remarks>
  607. /// <param name="character">Letter to show in the cube.</param>
  608. /// <param name="cycles">Number of rotations to make.</param>
  609. /// <param name="pause">How long (in milliseconds) between the movement from one angle to the next.</param>
  610. /// <returns>Animation ready for playing.</returns>
  611. private static void PrepareSpinningLetter(ArrayList animations, string character, int cycles, int pause = 20)
  612. {
  613. Debug.Print("Preparing spinning letter - " + character.ToString());
  614. ClearCube();
  615. byte[] dotPattern = LookupPattern(character);
  616. if (dotPattern != null)
  617. {
  618. for (int row = 0; row < 8; row++)
  619. {
  620. newFrame[(row * 8) + 3] = dotPattern[row];
  621. }
  622. }
  623. Animation spinningLetter = new Animation();
  624. spinningLetter.Frames = SpinBufferAroundYAxis(newFrame, 3, 3, 3);
  625. spinningLetter.Repeat = cycles;
  626. spinningLetter.FrameDelay = pause;
  627. animations.Add(spinningLetter);
  628. Debug.Print("Spinning letter complete.");
  629. }
  630. /// <summary>
  631. /// Display a spinning horizontal bar.
  632. /// </summary>
  633. /// <param name="animations">Animation collection to add this animation to.</param>
  634. /// <param name="cycles">Number of cycles to display the bar.</param>
  635. /// <param name="pause">Pause between angular rotations.</param>
  636. private static void PrepareSpinningBar(ArrayList animations, int cycles, int pause)
  637. {
  638. Debug.Print("Preparing spinning bar.");
  639. ClearCube();
  640. for (int x = 0; x < 8; x++)
  641. {
  642. SetPixel(x, 3, 3, true);
  643. }
  644. Animation spinningBar = new Animation();
  645. spinningBar.Frames = SpinBufferAroundYAxis(newFrame);
  646. spinningBar.Repeat = cycles;
  647. spinningBar.FrameDelay = pause;
  648. animations.Add(spinningBar);
  649. Debug.Print("Spinning bar complete.");
  650. }
  651. /// <summary>
  652. /// Spin a vertical plane.
  653. /// </summary>
  654. /// <param name="animations">Animation collection to add this animation to.</param>
  655. /// <param name="cycles">Number of time to repeat the animation.</param>
  656. /// <param name="pause">Pause between the angular rotations.</param>
  657. private static void PrepareSpinningPlanes(ArrayList animations, int cycles, int pause)
  658. {
  659. Animation leadInAnimation = new Animation();
  660. Animation leadOutAnimation = new Animation();
  661. Animation spinningAnimation = new Animation();
  662. Debug.Print("Preparing spinning plane.");
  663. leadInAnimation.Frames = new byte[8][];
  664. //
  665. // Now slide in the layer.
  666. //
  667. ClearCube();
  668. for (int x = 0; x < 8; x++)
  669. {
  670. for (int y = 0; y < 8; y++)
  671. {
  672. SetPixel(x, y, 7 - x, true);
  673. }
  674. leadInAnimation.Frames[x] = new byte[64];
  675. CopyFrame(newFrame, leadInAnimation.Frames[x]);
  676. }
  677. animations.Add(leadInAnimation);
  678. //
  679. // Setup the spinning animation.
  680. //
  681. ClearCube();
  682. for (int x = 0; x < 8; x++)
  683. {
  684. for (int y = 0; y < 8; y++)
  685. {
  686. SetPixel(x, y, 7 - x, true);
  687. }
  688. }
  689. spinningAnimation.Frames = SpinBufferAroundYAxis(newFrame);
  690. spinningAnimation.Repeat = cycles;
  691. spinningAnimation.FrameDelay = pause;
  692. animations.Add(spinningAnimation);
  693. //
  694. // Slide out the layer.
  695. //
  696. leadOutAnimation.Frames = new byte[8][];
  697. for (int x = 0; x < 8; x++)
  698. {
  699. for (int y = 0; y < 8; y++)
  700. {
  701. SetPixel(x, y, 7 - x, false);
  702. }
  703. leadOutAnimation.Frames[x] = new byte[64];
  704. CopyFrame(newFrame, leadOutAnimation.Frames[x]);
  705. }
  706. animations.Add(leadOutAnimation);
  707. Debug.Print("Spinning plane complete.");
  708. }
  709. /// <summary>
  710. /// Place a vertical plane in the cube and then split it in two horizontally. Spin the
  711. /// top half of the planani-clockwise around Y and spin the bottom half of the plane
  712. /// clockwise around the Y axis.
  713. /// </summary>
  714. /// <param name="animations">Animation collection to add this animation to.</param>
  715. /// <param name="cycles">Number of time to repeat the animation.</param>
  716. /// <param name="pause">Pause between the angular rotations.</param>
  717. private static void PrepareContraRotatingPlanes(ArrayList animations, int cycles, int pause)
  718. {
  719. Animation leadInAnimation = new Animation();
  720. Animation leadOutAnimation = new Animation();
  721. Animation spinningAnimation = new Animation();
  722. Debug.Print("Preparing contra rotating planes.");
  723. leadInAnimation.Frames = new byte[8][];
  724. //
  725. // Slide in the layer.
  726. //
  727. ClearCube();
  728. for (int x = 0; x < 8; x++)
  729. {
  730. for (int y = 0; y < 8; y++)
  731. {
  732. SetPixel(x, y, 7 - x, true);
  733. }
  734. leadInAnimation.Frames[x] = new byte[64];
  735. CopyFrame(newFrame, leadInAnimation.Frames[x]);
  736. }
  737. animations.Add(leadInAnimation);
  738. //
  739. // Setup the spinning animation.
  740. //
  741. ClearCube();
  742. for (int x = 0; x < 8; x++)
  743. {
  744. for (int y = 0; y < 8; y++)
  745. {
  746. SetPixel(x, y, 7 - x, true);
  747. }
  748. }
  749. spinningAnimation.Frames = SpinBufferAroundYAxis(newFrame);
  750. for (int frame = 15; frame >= 0; frame--)
  751. {
  752. for (int y = 0; y < 4; y++)
  753. {
  754. for (int x = 0; x < 8; x++)
  755. {
  756. for (int z = 0; z < 8; z++)
  757. {
  758. SetPixel(spinningAnimation.Frames[15 - frame], x, y, z, GetPixel(spinningAnimation.Frames[frame], x, 4, z));
  759. }
  760. }
  761. }
  762. }
  763. spinningAnimation.Repeat = cycles;
  764. spinningAnimation.FrameDelay = pause;
  765. animations.Add(spinningAnimation);
  766. //
  767. // Slide out the layer.
  768. //
  769. leadOutAnimation.Frames = new byte[8][];
  770. for (int x = 0; x < 8; x++)
  771. {
  772. for (int y = 0; y < 8; y++)
  773. {
  774. SetPixel(x, y, 7 - x, false);
  775. }
  776. leadOutAnimation.Frames[x] = new byte[64];
  777. CopyFrame(newFrame, leadOutAnimation.Frames[x]);
  778. }
  779. animations.Add(leadOutAnimation);
  780. Debug.Print("Spinning plane complete.");
  781. }
  782. /// <summary>
  783. /// Prepare a spinning cross made connecting the diagonals.
  784. /// </summary>
  785. /// <param name="animations">Animation collection to add this animation to.</param>
  786. /// <param name="cycles">Number of times to rotate the cross.</param>
  787. /// <param name="pause">Pause between the angular rotations.</param>
  788. private static void PrepareSpinningDiagonals(ArrayList animations, int cycles, int pause)
  789. {
  790. ClearBuffer(newFrame);
  791. for (int counter = 0; counter < 8; counter++)
  792. {
  793. SetPixel(counter, counter, counter, true);
  794. SetPixel(counter, 7 - counter, 7 - counter, true);
  795. SetPixel(7 - counter, 7 - counter, counter, true);
  796. SetPixel(7 - counter, counter, 7 - counter, true);
  797. }
  798. Animation spinningDiagonals = new Animation();
  799. spinningDiagonals.Frames = SpinBufferAroundYAxis(newFrame, 3.5F, 3.5F, 3.5F);
  800. spinningDiagonals.Repeat = cycles;
  801. spinningDiagonals.FrameDelay = pause;
  802. animations.Add(spinningDiagonals);
  803. }
  804. #endregion
  805. #region Cube methods.
  806. /// <summary>
  807. /// Slide a 4 x 4 cube around the bottom layer of the cube.
  808. /// </summary>
  809. private static void SlidingCube()
  810. {
  811. ClearCube();
  812. for (int x = 0; x < 4; x++)
  813. {
  814. for (int y = 0; y < 4; y++)
  815. {
  816. for (int z = 0; z < 4; z++)
  817. {
  818. SetPixel(x, y, z, true);
  819. }
  820. }
  821. }
  822. cube.UpdateBuffer(newFrame);
  823. Thread.Sleep(100);
  824. for (int x = 4; x <= 7; x++)
  825. {
  826. for (int y = 0; y < 4; y++)
  827. {
  828. for (int z = 0; z < 4; z++)
  829. {
  830. SetPixel(x, y, z, true);
  831. SetPixel(x - 4, y, z, false);
  832. }
  833. }
  834. cube.UpdateBuffer(newFrame);
  835. Thread.Sleep(100);
  836. }
  837. for (int z = 4; z <= 7; z++)
  838. {
  839. for (int x = 4; x <= 7; x++)
  840. {
  841. for (int y = 0; y < 4; y++)
  842. {
  843. SetPixel(x, y, z, true);
  844. SetPixel(x, y, z - 4, false);
  845. }
  846. }
  847. cube.UpdateBuffer(newFrame);
  848. Thread.Sleep(100);
  849. }
  850. for (int x = 7; x >= 4; x--)
  851. {
  852. for (int y = 0; y < 4; y++)
  853. {
  854. for (int z = 4; z <= 7; z++)
  855. {
  856. SetPixel(x, y, z, false);
  857. SetPixel(x - 4, y, z, true);
  858. }
  859. }
  860. cube.UpdateBuffer(newFrame);
  861. Thread.Sleep(100);
  862. }
  863. for (int z = 7; z >= 4; z--)
  864. {
  865. for (int x = 0; x <= 4; x++)
  866. {
  867. for (int y = 0; y <= 4; y++)
  868. {
  869. SetPixel(x, y, z - 4, true);
  870. SetPixel(x, y, z, false);
  871. }
  872. }
  873. cube.UpdateBuffer(newFrame);
  874. Thread.Sleep(100);
  875. }
  876. }
  877. /// <summary>
  878. /// Create a pulsating cube animation.
  879. /// </summary>
  880. /// <param name="animations">Animations collaction to add this animation to.</param>
  881. /// <param name="cycles">Number of times to repeat the animation.</param>
  882. /// <param name="pause">Number of milliseconds to pause between the frames.</param>
  883. private static void PreparePulsingCube(ArrayList animations, int cycles, int pause)
  884. {
  885. Animation pulsingCube = new Animation();
  886. pulsingCube.Frames = new byte[6][];
  887. for (int lower = 3; lower > 0; lower--)
  888. {
  889. pulsingCube.Frames[3 - lower] = new byte[64];
  890. for (int x = lower; x <= 7 - lower; x++)
  891. {
  892. for (int y = lower; y <= 7 - lower; y++)
  893. {
  894. for (int z = lower; z <= 7 - lower; z++)
  895. {
  896. SetPixel(pulsingCube.Frames[3 - lower], x, y, z, true);
  897. }
  898. }
  899. }
  900. }
  901. for (int counter = 0; counter < 3; counter++)
  902. {
  903. pulsingCube.Frames[5 - counter] = new byte[64];
  904. CopyFrame(pulsingCube.Frames[counter], pulsingCube.Frames[5 - counter]);
  905. }
  906. pulsingCube.Repeat = cycles;
  907. pulsingCube.FrameDelay = pause;
  908. animations.Add(pulsingCube);
  909. }
  910. #endregion
  911. #region Fireworks.
  912. /// <summary>
  913. /// Add the fireworks to the cube frame buffer.
  914. /// </summary>
  915. /// <param name="fireworks">Array of fireworks to add.</param>
  916. private static void AddFireworksToBuffer(Firework[] fireworks)
  917. {
  918. ClearCube();
  919. foreach (Firework firework in fireworks)
  920. {
  921. if (firework.IsClimbing)
  922. {
  923. SetPixel(firework.X, firework.CurrentHeight, firework.Z, true);
  924. }
  925. if (firework.IsExploding)
  926. {
  927. foreach (Point shard in firework.Shards)
  928. {
  929. SetPixel(shard.X, shard.Y, shard.Z, true);
  930. }
  931. }
  932. }
  933. }
  934. /// <summary>
  935. /// Create a firework display in the cube.
  936. /// </summary>
  937. /// <param name="numberOfFireworks">Number of fireworks to add to the cube.</param>
  938. /// <param name="noCycles">Number of cycles to run this animation for.</param>
  939. private static void FireworkDisplay(int numberOfFireworks, int noCycles = 10)
  940. {
  941. Firework[] fireworks = new Firework[numberOfFireworks];
  942. for (int counter = 0; counter < numberOfFireworks; counter++)
  943. {
  944. fireworks[counter] = new Firework();
  945. }
  946. bool processing = true;
  947. while (processing)
  948. {
  949. processing = false;
  950. for (int counter = 0; counter < numberOfFireworks; counter++)
  951. {
  952. processing |= fireworks[counter].Move();
  953. }
  954. if (processing)
  955. {
  956. AddFireworksToBuffer(fireworks);
  957. cube.UpdateBuffer(newFrame);
  958. Thread.Sleep(100);
  959. }
  960. }
  961. }
  962. #endregion
  963. /// <summary>
  964. /// Main program loop.
  965. /// </summary>
  966. public static void Main()
  967. {
  968. #region Setup the character set.
  969. characterSet[0] = new PlanarBitmap("A", new byte[] { 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x1C, 0x0 });
  970. characterSet[1] = new PlanarBitmap("B", new byte[] { 0x1E, 0x22, 0x22, 0x1E, 0x22, 0x22, 0x1E, 0x0 });
  971. characterSet[2] = new PlanarBitmap("C", new byte[] { 0x1C, 0x22, 0x2, 0x2, 0x2, 0x22, 0x1C, 0x0 });
  972. characterSet[3] = new PlanarBitmap("D", new byte[] { 0xE, 0x12, 0x22, 0x22, 0x22, 0x12, 0xE, 0x0 });
  973. characterSet[4] = new PlanarBitmap("E", new byte[] { 0x3E, 0x2, 0x2, 0x1E, 0x2, 0x2, 0x3E, 0x0 });
  974. characterSet[5] = new PlanarBitmap("F", new byte[] { 0x2, 0x2, 0x2, 0x1E, 0x2, 0x2, 0x3E, 0x0 });
  975. characterSet[6] = new PlanarBitmap("G", new byte[] { 0x1C, 0x22, 0x22, 0x3A, 0x2, 0x22, 0x1C, 0x0 });
  976. characterSet[7] = new PlanarBitmap("H", new byte[] { 0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x0 });
  977. characterSet[8] = new PlanarBitmap("I", new byte[] { 0x3E, 0x8, 0x8, 0x8, 0x8, 0x8, 0x3E, 0x0 });
  978. characterSet[9] = new PlanarBitmap("J", new byte[] { 0xC, 0x12, 0x10, 0x10, 0x10, 0x10, 0x38, 0x0 });
  979. characterSet[10] = new PlanarBitmap("K", new byte[] { 0x22, 0x12, 0xA, 0x6, 0xA, 0x12, 0x22, 0x0 });
  980. characterSet[11] = new PlanarBitmap("L", new byte[] { 0x3E, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x0 });
  981. characterSet[12] = new PlanarBitmap("M", new byte[] { 0x22, 0x22, 0x22, 0x2A, 0x2A, 0x36, 0x22, 0x0 });
  982. characterSet[13] = new PlanarBitmap("N", new byte[] { 0x22, 0x22, 0x32, 0x2A, 0x26, 0x22, 0x22, 0x0 });
  983. characterSet[14] = new PlanarBitmap("O", new byte[] { 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x0 });
  984. characterSet[15] = new PlanarBitmap("P", new byte[] { 0x2, 0x2, 0x2, 0x1E, 0x22, 0x22, 0x1E, 0x0 });
  985. characterSet[16] = new PlanarBitmap("Q", new byte[] { 0x2C, 0x12, 0x2A, 0x22, 0x22, 0x22, 0x1C, 0x0 });
  986. characterSet[17] = new PlanarBitmap("R", new byte[] { 0x22, 0x12, 0xA, 0x1E, 0x22, 0x22, 0x1E, 0x0 });
  987. characterSet[18] = new PlanarBitmap("S", new byte[] { 0x1E, 0x20, 0x20, 0x1C, 0x2, 0x2, 0x3C, 0x0 });
  988. characterSet[19] = new PlanarBitmap("T", new byte[] { 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x3E, 0x0 });
  989. characterSet[20] = new PlanarBitmap("U", new byte[] { 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x0 });
  990. characterSet[21] = new PlanarBitmap("V", new byte[] { 0x8, 0x14, 0x22, 0x22, 0x22, 0x22, 0x22, 0x0 });
  991. characterSet[22] = new PlanarBitmap("W", new byte[] { 0x14, 0x2A, 0x2A, 0x2A, 0x22, 0x22, 0x22, 0x0 });
  992. characterSet[23] = new PlanarBitmap("X", new byte[] { 0x22, 0x22, 0x14, 0x8, 0x14, 0x22, 0x22, 0x0 });
  993. characterSet[24] = new PlanarBitmap("Y", new byte[] { 0x8, 0x8, 0x8, 0x8, 0x36, 0x22, 0x22, 0x0 });
  994. characterSet[25] = new PlanarBitmap("Z", new byte[] { 0x3E, 0x2, 0x4, 0x8, 0x10, 0x20, 0x3E, 0x0 });
  995. characterSet[26] = new PlanarBitmap("RightArrow", new byte[] { 0x10, 0x30, 0x7F, 0xFF, 0x70, 0x30, 0x10, 0x0 });
  996. characterSet[27] = new PlanarBitmap("Circle", new byte[] { 0x18, 0x3C, 0x7E, 0xFF, 0xFF, 0x7E, 0x3C, 0x18 });
  997. characterSet[28] = new PlanarBitmap("SnowFlake", new byte[] { 0x99, 0x42, 0x24, 0x99, 0x99, 0x24, 0x42, 0x99 });
  998. #endregion
  999. ClearCube();
  1000. Thread display = new Thread(new ThreadStart(cube.DisplayBuffer));
  1001. display.Start();
  1002. ArrayList animations = new ArrayList();
  1003. PreparePulsingCube(animations, 2, 200);
  1004. PrepareSpinningLetter(animations, "S", 2, 100);
  1005. PrepareSpinningLetter(animations, "P", 2, 100);
  1006. PrepareSpinningLetter(animations, "I", 2, 100);
  1007. PrepareSpinningLetter(animations, "N", 2, 100);
  1008. PrepareSpinningPlanes(animations, 2, 100);
  1009. PrepareContraRotatingPlanes(animations, 2, 100);
  1010. PrepareSpinningLetter(animations, "Circle", 4, 100);
  1011. PrepareSpinningLetter(animations, "SnowFlake", 4, 100);
  1012. while (true)
  1013. {
  1014. ShowMessage("NETDUINOROCKS");
  1015. TriangularWave(4);
  1016. MexicanWave(4);
  1017. foreach (Animation animation in animations)
  1018. {
  1019. animation.Play(cube);
  1020. }
  1021. Rain(10, 100);
  1022. for (int counter = 0; counter < 10; counter++)
  1023. {
  1024. FireworkDisplay(1);
  1025. }
  1026. }
  1027. }
  1028. }
  1029. }