top of page

Programming

Below you will find some examples of code and screens I designed.

Pinouts v4.jpg

Blink LEDs via Raspberry Pi 4 / 5

Here is a little project for those who want to get into writing .NET 8 console apps on your Raspberry Pi 4 or 5.

I used a breadboard and jumpers all from amazon. Search for: 

"HUAREW Breadboard and Jumper Wires Kit Include 830 Tie Points Breadboard 400 Tie Points Breadboard Jumper Wire"

Follow the diagram and download the .NET code here.

There are many documents out there that will tell you how to install .NET 8 onto your Pi. Here are a few...

https://github.com/dotnet/iot/blob/main/samples/led-blink-multiple/Program.cs

https://www.youtube.com/watch?v=TS4DNGByIoc

https://gist.github.com/ramonsmits/b15d97965bcfacc19920be2e84b49c4e

Encryption Example in C#

Below is the entire class I use. to encrypt and decrypt database connections strings stored in the app.config file.

 

This is what I use to decrypt the string...

Crypto.Decrypt(ConfigIO.SettingsRead(ConnectConnection), Var.SQINPfeKwB4)

Var.SQINPfeKwB4 is used to store the key. It's broken up into 16 segments throughout the program. The key is 28 characters long. I also use longer keys.

using System;
using System.Text;
using System.Security.Cryptography;

public class Crypto
{
  private const int Keysize = 256;
  private const int DerivationIterations = 1000;
  public static string Encrypt(string plainText, string passPhrase)
  {
    System.Security.Cryptography.RijndaelManaged AES = new System.Security.Cryptography.RijndaelManaged();
    System.Security.Cryptography.MD5CryptoServiceProvider Hash_AES = new System.Security.Cryptography.MD5CryptoServiceProvider();
    string encrypted = "";
    try
    {
      byte[] hash = new byte[32];
      byte[] temp = Hash_AES.ComputeHash(System.Text.ASCIIEncoding.ASCII.GetBytes(passPhrase));
      Array.Copy(temp, 0, hash, 0, 16);
      Array.Copy(temp, 0, hash, 15, 16);
      AES.Key = hash;
      AES.Mode = System.Security.Cryptography.CipherMode.ECB;
      System.Security.Cryptography.ICryptoTransform DESEncrypter = AES.CreateEncryptor();
      byte[] Buffer = System.Text.ASCIIEncoding.ASCII.GetBytes(plainText);
      encrypted = Convert.ToBase64String(DESEncrypter.TransformFinalBlock(Buffer, 0, Buffer.Length));
      return encrypted;
    }
    catch
    {
      return string.Empty;
    }
  }
  public static string Decrypt(string plainText, string passPhrase)
  {
    System.Security.Cryptography.RijndaelManaged AES = new System.Security.Cryptography.RijndaelManaged();
    System.Security.Cryptography.MD5CryptoServiceProvider Hash_AES = new System.Security.Cryptography.MD5CryptoServiceProvider();
    string decrypted = "";
    try
    {
      byte[] hash = new byte[32];
      byte[] temp = Hash_AES.ComputeHash(System.Text.ASCIIEncoding.ASCII.GetBytes(passPhrase));
      Array.Copy(temp, 0, hash, 0, 16);
      Array.Copy(temp, 0, hash, 15, 16);
      AES.Key = hash;
      AES.Mode = System.Security.Cryptography.CipherMode.ECB;
      System.Security.Cryptography.ICryptoTransform DESDecrypter = AES.CreateDecryptor();
      byte[] Buffer = Convert.FromBase64String(plainText);
      decrypted = System.Text.ASCIIEncoding.ASCII.GetString(DESDecrypter.TransformFinalBlock(Buffer, 0, Buffer.Length));
      return decrypted;
    }
    catch
    {
      return string.Empty;
    }
  }
  public static string RNGCharacterMask(int maxSize)
  {
    string keySigns = String.Empty;
    keySigns = "aAbBcCdDeEfFgGhHijJkKLmMnNpPqQrRsTtUuVvWwXxYyz2346789";
    char[] chars = new char[keySigns.Length + 1];
    chars = keySigns.ToCharArray();
    int size = maxSize;
    byte[] data = new byte[] { 1 };
    RNGCryptoServiceProvider crypto = new RNGCryptoServiceProvider();
    crypto.GetNonZeroBytes(data);
    size = maxSize;
    data = new byte[size - 1 + 1];
    crypto.GetNonZeroBytes(data);
    StringBuilder result = new StringBuilder(size);
    foreach (byte b in data)
    {
      result.Append(chars[b % (chars.Length - 1)]);
    }

    return result.ToString();
  }
  public static string HASHEncrypt(string Password, bool x, string Salt, out string SaltReturn)
  {
    if (x == true)
    {
      Salt = GetRandomSalt(Password.Length);
    }

    var combinedPassword = string.Concat(Password, Salt);
    var sha512 = new SHA512Managed();
    var bytes = UTF8Encoding.UTF8.GetBytes(combinedPassword);
    var hash = sha512.ComputeHash(bytes);

    SaltReturn = Salt;
    return Convert.ToBase64String(hash);
  }
  private static string GetRandomSalt(Int32 size)
  {
    var random = new RNGCryptoServiceProvider();
    var salt = new byte[size];
    random.GetBytes(salt);
    return Convert.ToBase64String(salt);
  }
}

Using this method of encryption, I created a database with over 1 million GUID 32 entries with no repeats. The program I designed can produce 100's GUID 32 entries per second. It can also produce keys with - or without.

76357C64-C1E3-4EAC-917B-5585042E2095
9576F311-086D-4DC0-BD5B-77C2187C7DDC
7EF5B568-718E-452A-BBF1-AEEAB5578C9B
5E273733-1C6B-4669-AC66-E44692439135
E8179871-26EA-4035-87FB-C3C874C610E4
A236EFF8-47D8-4D91-9800-51EFA8A1B990
11C10F40-CAC4-419A-9C3F-ABA93C24D697
E7EC8B7A-720E-49DA-B08E-42C8454B7578
CFC43E7E-8979-439E-B807-E80A5AD5CB4A
5E0D88E3-12C0-4158-B924-7092C98CC064

6A2E282628C7494C8170D4B8F123476E
3C0926D5D3564537970F17B78B11C4B8
4102E1A1837F4CAF84EFA1A94B7D87B0
39BBEB42360D400F8F549FA37A1E2096
446E59CC9DF349C7BF29F83450A9EA0D
46E906523D6842918673E281A5347A30
F80F310454D147388C04983309C61CAD
43C05412A73B476796183FF5ACBBBE80
F2D31587C9244500A8BF34DE21C17827
09890892C3B24B0D813058919BA73F76

It can also produce special keys for encryption using:

ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789
ABCDEFGHIJKLMNOPQRSTUVWXYZ
AaBbCcDdEeFfGgHhIiJjKkLMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789
ABCDEFGHIJKLMNOPQRSTUVWXYZ?,./:;}{][!@#$%^*()_=+-
AaBbCcDdEeFfGgHhIiJjKkLMmNnOoPpQqRrSsTtUuVvWwXxYyZz?,./:;}{][!@#$%^*()_=+-
ABCDEFGHIJKLMNOPQRSTUVWXYZ?,./:;}{][0123456789!@#$%^*()_=+-

and many other combinations. The longest and most complex key type has over 140 characters.

32 Keys (non GUID 32)...

/S0D?V13^38R,WZMWHG7NTYY@*23^NBK
^;OQS@?QI#=Y(S,MC$V9[;+W3#7AGL01
??PW@]%:Q+)NXRE{T)Z;YNXGIO{W8YXW
=EQM0%Y;KG[X3*7Y4SL%IA@7D:EF5,HF
B**NV5V*AY=3O.}6Q@/+RRW/8S:H_SPS
SEI%*?06ML+U_:Q62D)$FP3NP)X:T0^2
H!H$6]E,TS79SD4TXA(M;5VNX$^EJ8LC
D1I=BKU7.={F)5QI5%%/CWMTG._CZ58Y
:Q3U1PZ2[WF+C@RUSLH@TZ3ZNYYH):A1
9H,@01@L]+K7!S6?[)%)NJCY1!=RPM7I

64 Keys...

PH5{2)Z9L$#3^:3^6WM7PP)_,1EHB9(MQU2JM2/@T?G5C*^B*O/U9JPKS=]%XHSO
4:0XJ9NSS[0M4X:U7TI=QHUQMCD;Z$}YE]8!J}]@{_OX*L]5_$IST]2X((:^MFW:
{+!F#6AJER_]BI,(U88X!+K)G%GWJFL^C20#D{+B3M$$_A+F9Q8%I]]D6;E#W{J7
/)RP]N?6JH}Y64!@;U7,F0(S!ME::Z3ZI#:FM+X^G2)Q*)R;H+?IQHKX8W4IG:Y}
H6/3#C/_R7X)=+%DN?SC$/;U%K$NE.:YBK84L^T!P(I[:P:0]3D!XL]G80V9MZM[
VG]9]ZGAWMU1KU42RZ}HK%DEC^[A8U:/+*ECQCMQL9GMX$CYZUUA#,.G8(ET8%L[
/D7_V!([]5E#R;FLUST}ZPG.G#T?A@A2)C{4M[/.]]J#GHE(+K9QP_OKS91I9^[0
Z.^49LG/F*M,^{[G5=IDBBQ?_QMCJIFJ}PJV8/JB{!52(4LKGEX7{{P{Q%S(3N/=
!R9)RSD+VVRHJJTWF$*T]_^$,814*H3M+C+H;UG}#WK^;LP;D#R6K}57NV^T7LRJ
%0?J7TN1?%6KK=9I%.3+IZ**/UH)EL)E*:ZQG1T[U.8330XG,BY7M}0=[FXU;NWE

128 Keys...

(RN+T4F*EUKJYN@]?R!2V4Y9L=M=[U3:#F%9RGHI(%_=8#KE$:?X{@{M9BPF@;@94PPE(C]UHIZIH9NR;[A({;_+,0)SRH3C_BWN*43@LWM33GLSU+?{{VP,:A7R!^Y@X
8Q$#3WCDT/TRF{5NS+K:*%=1%6G^:6G7@5Y]G/%KG^=^8R4X0BTA#WB)/!^GBC;V$A7LMCV}[P1H?)/B$93XJLP[S*WL%]1//D^I2N?K_0EMPTLF8S.L8SCMMQE0)3[J
CVBG!ZMBMT8VW{D4[0LXT{VY]K=42@8{3]FG#9}W?4BY(8]S*1?5HV?URS#T.I88+UA#W+ER.U90K@HH.GZ]T[U)^L0.@_,L3X%Z;CLTJQNUL,;C]A8M;$IMBTQH8A}!
2^+Y?2BIK;!L*V:QRG@$C0*M?U}QI{=*QVW/]@0VTB!1;/TKC75;?;({$XR=VQ{:Q^4S2S+2.QJ3.BH@{C!AY:;O$4D@#DXE%:VQ42!EJ#$)2NR$D+K;=$PFAQW^^3R;
KP/)*OOMFYP)XE,7O./0BYC5,4V@A%N6_6.MS7BJ9?;N$*5P28N7[Z2L1FH*CVTKVYR1B5=I7SD5UQ]760BGC]C{I4%FMAWL0P#[1U#0F)ORQUC9B(V_1QEJLY@PM$K:
0RMT*6S%SH%,LBG%!%YX$3!FC/D_EJ26J;}ZN#04_SRA}J=+.:UV;C7]P;8C#Q%!X+},GY]GOQRTIM!)WA]B^ZMGWN$@)M5LE?{/19W)P.%)S%4S%_!N_$BXE2KNUU)^
=5ON#EJC8.:R9..DQ6.NMYBX}AZHCQ,I4W2_L_B_?^)(95NPL*E]BE_1;0#^Q:+/],;:RSO57Z;G;8!!,!7X0O;,BU68?KP7W^Q_*UG(X6IQH/UGX81}!](;}]@QMEX;
/^X{IXRVV0:^+NRIEN#3):2]A8H.*)^*]ZJU(D@WT1PJ/?.D:,6FNT0FU]*0:OGLZDX#B(EQNW_[[GL%=EQY+O]:=KCC.TI.#PD!.!Q{R!:^+J?};UL4AL$(KNE,77ON
W38L:KCWJ83^Y93GJD0RNV14U6M$@{*[TATX1C=HB!5RZ{V.AKW[{?+T[4{=LYT#.22I[QCJ?:I+=RS?O0UZTY?,WS/#_[!]442!={3@/9,PX[AIU3,A*J^3_X,FWD22

W8S;03O[1KK/[DU8U2M?#U}BYF4]G#WO%*1,1TG6E%6]K0K(Z4T2USFA_(H,.BUM7QERR0O^@K%4%.6L_9KUQ{]J*%0$6QH0A?,:)NFAUG3B@F)3YU4N}^JJ$UBI%JQ3

RotatingCubeEmpty.png

The below code is the code behind for the main screen. It's not all the code but you should be able to add your own.

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Threading;

using System.IO;
using System.Windows.Media.Media3D;
using System.Diagnostics;
using RotatngCube;
using System.Reflection;
using DialogMessage;

namespace RotatingCube
{

...

}

A new collection screen. This is where the user would add 6 new images, 256x256 pixels in size. If done correctly the user can wrap some of the images together to make a very cool effect.

    #region "Vars "
    private double hScrollBar = -180;
    private double vScrollBar = -180;
    private bool hAxis = true;
    private bool vAxis = true;
    private bool xRandom = false;
    private int hBumper = 90;
    private int vBumper = 90;
    private int hDirection = 1;
    private int vDirection = 1;
    DispatcherTimer RotationTimer = new DispatcherTimer();
    DispatcherTimer CollectionTimer = new DispatcherTimer();
    private int CollectionIndex = 1;
    private bool FullScreen = false;
    #endregion

From the menu, the user has control over what's shown and how. They can also select if they want to see the entire collection of images (cubes) and wait for x seconds before changing to the next collection.

    #region " Main "
    private void Window_Loaded(object sender, RoutedEventArgs e)
    {
      string txt = "-1, -1, -1";
      string[] values = txt.Split(',');
      float x = 2.5f * float.Parse(values[0]);
      float y = 2.5f * float.Parse(values[1]);
      float z = 2.5f * float.Parse(values[2]);
      float yup = y > 0 ? 1 : -1;
      PositionCamera(x, y, z, yup);

      RotationTimer.Tick += RotationTimer_Tick;
      CollectionTimer.Tick += CollectionTimer_Tick;

      if (ConfigIO.SettingsRead("First Run") == "Yes")
      {
        var ConfigForm = new Config();
        ConfigForm.ShowDialog();
      }

      LoadImages("Last Collection");
      LoadDefaults();

      if (ConfigIO.SettingsRead("Sequential Collections") == "Yes")
      {
        mnuSequentialCollections.IsChecked = true;
        CollectionTimerStart();
      }
    }
    private void Window_KeyDown(object sender, KeyEventArgs e)
    {
      if (e.Key == Key.F11)
      {
        if (FullScreen == false)
        {
          this.WindowState = WindowState.Normal;
          this.WindowStyle = WindowStyle.None;
          MainMenu.Visibility = Visibility.Hidden;
          this.WindowState = WindowState.Maximized;
          FullScreen = true;
        }
        else
        {
          this.WindowState = WindowState.Normal;
          this.WindowStyle = WindowStyle.SingleBorderWindow;
          MainMenu.Visibility = Visibility.Visible;
          if (ConfigIO.SettingsRead("Last Screen State") == "Maximize")
          {
            WindowState = WindowState.Maximized;
          }
          FullScreen = false;
        }
      }
    }
    #endregion

Another menu item... The user can select auto-rotation and on what axis they want to rotate. They can also select if they want the computer to auto-rotate the cube.

    #region " Timers "
    private void RotationTimerStart()
    {
      hScrollBar = hscroll.Value;
      vScrollBar = vscroll.Value;

      RotationTimer.Interval = TimeSpan.FromMilliseconds(Conv.ToInteger(RotationSpeed.Text));
      RotationTimer.Start();
    }
    private void RotationTimer_Tick(object sender, EventArgs e)
    {
      if (xRandom == true)
      {
        Random xyRandom = new Random();

        if ((mnuRotateX.IsChecked != true) || (mnuRotateY.IsChecked != true))
        {
          mnuRotateX.IsChecked = true;
          hAxis = true;
          mnuRotateY.IsChecked = true;
          vAxis = true;
        }

        // Where is hScrollbar
        if (hScrollBar == hBumper)
        {
          hBumper = xyRandom.Next(-180, 180);
          if (hBumper >= 0)
          {
            hDirection = 1;
          }
          else
          {
            hDirection = -1;
          }
        }

        if (vScrollBar == vBumper)
        {
          vBumper = xyRandom.Next(-180, 180);
          if (vBumper >= 0)
          {
            vDirection = 1;
          }
          else
          {
            vDirection = -1;
          }
        }
      }

      if (hAxis == true)
      {
        if (hScrollBar == 180)
        {
          hScrollBar = -180;
          hDirection = 1;
        }
        else if (hScrollBar == -180)
        {
          hScrollBar = 180;
          hDirection = -1;
        }

        hScrollBar += hDirection * 1;
        hscroll.Value = hScrollBar;
      }

      if (vAxis == true)
      {
        if (vScrollBar == 180)
        {
          vScrollBar = -180;
          vDirection = 1;
        }
        else if (vScrollBar == -180)
        {
          vScrollBar = 180;
          vDirection = -1;
        }

        vScrollBar += vDirection * 1;
        vscroll.Value = vScrollBar;
      }

      if (this.WindowStyle != WindowStyle.None)
      {
        if (mnuShowXYZ.IsChecked == true)
        {
          if (hScrollBar < 0)
          {
            X_Menu.Header = "X = " + hScrollBar.ToString("000");
          }
          else
          {
            X_Menu.Header = "X = " + hScrollBar.ToString(" 000");
          }

          if (vScrollBar < 0)
          {
            Y_Menu.Header = "Y = " + vScrollBar.ToString("000");
          }
          else
          {
            Y_Menu.Header = "Y = " + vScrollBar.ToString(" 000");
          }

          Z_Menu.Header = "Z = " + ImagesZoom.Text;
        }
      }
    }
    private void CollectionTimerStart()
    {
      CollectionTimer.Interval = TimeSpan.FromSeconds(ConfigIO.SettingsReadInteger("Sequential Timer Seconds"));
      CollectionTimer.Start();
    }
    private void CollectionTimer_Tick(object sender, EventArgs e)
    {
      CollectionIndex += 1;
      string ImageCollection = ConfigIO.SettingsRead("Images" + CollectionIndex.ToString());
      if (string.IsNullOrEmpty(ImageCollection))
      {
        CollectionIndex = 2;
        ImageCollection = ConfigIO.SettingsRead("Images" + CollectionIndex.ToString());
      }
      LoadImages(ImageCollection);
    }
    #endregion

    #region " Functions "
    // Move the camera to the indicated position looking back at the origin.
    private void PositionCamera(float x, float y, float z, float yup)
    {
      hscroll.Value = 0;
      vscroll.Value = 0;
      PerspectiveCamera the_camera = viewCube.Camera as PerspectiveCamera;
      the_camera.Position = new Point3D(x, y, z);
      the_camera.LookDirection = new Vector3D(-x, -y, -z);
      the_camera.UpDirection = new Vector3D(0, yup, 0);
    }
    // Change Images
    private void ChangeImage(string xImages)
    {
      try
      {
        var uriBack = new Uri(Var.DefaultPath + @"\Images\" + xImages + @"\Back.png", UriKind.Relative);
        var bitmapBack = new BitmapImage(uriBack);
        ImgBack.ImageSource = bitmapBack;

        var uriBottom = new Uri(Var.DefaultPath + @"\Images\" + xImages + @"\Bottom.png", UriKind.Relative);
        var bitmapBottom = new BitmapImage(uriBottom);
        ImgBottom.ImageSource = bitmapBottom;

        var uriFront = new Uri(Var.DefaultPath + @"\Images\" + xImages + @"\Front.png", UriKind.Relative);
        var bitmapFront = new BitmapImage(uriFront);
        ImgFront.ImageSource = bitmapFront;

        var uriLeft = new Uri(Var.DefaultPath + @"\Images\" + xImages + @"\Left.png", UriKind.Relative);
        var bitmapLeft = new BitmapImage(uriLeft);
        ImgLeft.ImageSource = bitmapLeft;

        var uriRight = new Uri(Var.DefaultPath + @"\Images\" + xImages + @"\Right.png", UriKind.Relative);
        var bitmapRight = new BitmapImage(uriRight);
        ImgRight.ImageSource = bitmapRight;

        var uriTop = new Uri(Var.DefaultPath + @"\Images\" + xImages + @"\Top.png", UriKind.Relative);
        var bitmapTop = new BitmapImage(uriTop);
        ImgTop.ImageSource = bitmapTop;
      }
      catch (Exception ex)
      {
        if (ex.Message.Contains("File"))
        {
          ShowMessage.Dialog("File Error" + Environment.NewLine + ex.Message, DialogImage.Critical, "File Error");
        }
        else
        {
          ShowMessage.Dialog(ex.Message, DialogImage.Critical, "File Error");
        }
      }
    }
    private void LoadImages(string Collection)
    {
      if (Collection == "Last Collection")
      {
        Collection = ConfigIO.SettingsRead("Last Collection");
      }

      int ImageCount = 1;
      mnuImages.Items.Clear();

      // Get last Image Collected used
      string LastCollection = Collection;

      for (ImageCount = 1; ImageCount <= 150; ImageCount++)
      {
        string Image = ConfigIO.SettingsRead("Images" + ImageCount.ToString());

        if (!string.IsNullOrEmpty(Image))
        {
          MenuItem newMenuItem2 = new MenuItem();
          MenuItem newExistMenuItem = (MenuItem)this.mnuImage.Items[0];
          newMenuItem2.Header = Image;
          newMenuItem2.Click += new RoutedEventHandler(mnuChange_Click);
          newMenuItem2.IsCheckable = true;
          if (Image == LastCollection)
          {
            newMenuItem2.IsChecked = true;
            ChangeImage(Image);
          }
          else
          {
            newMenuItem2.IsChecked = false;
          }
          newMenuItem2.Name = "mnuCollection" + ImageCount.ToString();
          newExistMenuItem.Items.Add(newMenuItem2);
        }
        else
        {
          break;
        }
      }
    }
    // Load and set defaults
    private void LoadDefaults()
    {
      // Get defaults
      RotationSpeed.Text = ConfigIO.SettingsRead("Rotation Speed");
      ImagesZoom.Text = ConfigIO.SettingsRead("Last Zoom Factor");

      if (ConfigIO.SettingsRead("Auto Start") == "Yes")
      {
        mnuRotation.IsChecked = true;
        RotationAutoStart();
      }

      // What screen to use
      string xDefaultMonitor = ConfigIO.SettingsRead("Default Monitor");
      //System.Windows.Forms.Screen s = System.Windows.Forms.Screen.FromPoint(System.Windows.Forms.Cursor.Position);
      System.Windows.Forms.Screen[] screens = System.Windows.Forms.Screen.AllScreens;
      for (int i = 0; i < screens.Length; i++)
      {
        if (screens[i].DeviceName.Contains("DISPLAY" + xDefaultMonitor))
        {
          Left = (screens[i].Bounds.Left - (this.Width / 2)) + (screens[i].Bounds.Width / 2);
        }
      }
      if (ConfigIO.SettingsRead("Last Screen State") == "Maximize")
      {
        WindowState = WindowState.Maximized;
      }
    }
    private void RotationAutoStart()
    {
      // Rotatation Timer
      if (mnuRotation.IsChecked == true)
      {
        float xZoomFactor = float.Parse(ImagesZoom.Text);
        string txt = "-1, -1, -1";
        string[] values = txt.Split(',');
        float x = xZoomFactor * float.Parse(values[0]);
        float y = xZoomFactor * float.Parse(values[1]);
        float z = xZoomFactor * float.Parse(values[2]);
        float yup = y > 0 ? 1 : -1;
        PositionCamera(x, y, z, yup);
        hscroll.Visibility = Visibility.Hidden;
        vscroll.Visibility = Visibility.Hidden;

        RotationTimerStart();
      }
      else
      {
        RotationTimer.Stop();
        hscroll.Visibility = Visibility.Visible;
        vscroll.Visibility = Visibility.Visible;
      }
      // Random
      if (mnuRandom.IsChecked == true)
      {
        xRandom = mnuRandom.IsChecked;
      }
    }
    private bool CheckForExistingInstance()
    {
      bool ReturnValue = false;
      if ((ConfigIO.SettingsRead("More than one Instance") == "No") && (Process.GetProcessesByName(Process.GetCurrentProcess().ProcessName).Length > 1))
      {
        ReturnValue = true;
      }
      return ReturnValue;
    }
    #endregion

3D Rotating Cube

ftp_icon24.png

This project took some time to complete. Below is the main screen in WPF.

<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:System="clr-namespace:System;assembly=mscorlib" xmlns:av="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="av" x:Class="RotatingCube.Main" Title="3d Rotating Cube" Height="800" Width="800" Icon="RotatingCube.ico" Loaded="Window_Loaded" KeyDown="Window_KeyDown">
  <DockPanel Margin="0" Background="Black">
    <Menu DockPanel.Dock="Top" x:Name="MainMenu" Visibility="Visible" FontFamily="Consolas" FontSize="14" Cursor="Hand">
      <MenuItem Foreground="Purple" Header="File" x:Name="mnuSave" Click="mnuSave_Click">
        <System:String>Save</System:String>
      </MenuItem>
      <MenuItem Foreground="ForestGreen" Header="View">
        <MenuItem Header="(1, 1, 1)" x:Name="mnuView111" Click="mnuView_Click" />
        <MenuItem Header="(1, 1, -1)" x:Name="mnuView11_1" Click="mnuView_Click" />
        <MenuItem Header="(-1, 1, -1)" x:Name="mnuView_11_1" Click="mnuView_Click" />
        <MenuItem Header="(-1, 1, 1)" x:Name="mnuView_111" Click="mnuView_Click" />

        <MenuItem Header="(1, -1, 1)" x:Name="mnuView1_11" Click="mnuView_Click" />
        <MenuItem Header="(1, -1, -1)" x:Name="mnuView1_1_1" Click="mnuView_Click" />
        <MenuItem Header="(-1, -1, -1)" x:Name="mnuView_1_1_1" Click="mnuView_Click" />
        <MenuItem Header="(-1, -1, 1)" x:Name="mnuView_1_11" Click="mnuView_Click" />
      </MenuItem>
      <MenuItem Foreground="Blue" Header="Rotate">
        <MenuItem Header="Rotate X" x:Name="mnuRotateX" IsCheckable="True" IsChecked="True" Click="mnuRotate_Click" />
        <MenuItem Header="Rotate Y" x:Name="mnuRotateY" IsCheckable="True" IsChecked="True" Click="mnuRotate_Click" />
        <MenuItem Header="Random" x:Name="mnuRandom" IsCheckable="True" IsChecked="True" Click="mnuRotate_Click" />
        <MenuItem Header="Rotation" x:Name="mnuRotation" IsCheckable="True" IsChecked="False" Click="mnuRotate_Click" />
        <Separator />
        <StackPanel Orientation="Horizontal">
          <Label x:Name="BaseLabel" Content="Speed:" HorizontalAlignment="Left" Height="28px" Margin="1" />
          <TextBox x:Name="RotationSpeed" TextWrapping="NoWrap" Width="30" Text="30" HorizontalAlignment="Left" Margin="1" Height="18px" />
        </StackPanel>
      </MenuItem>
      <MenuItem Foreground="DarkCyan" Header="New Collection" x:Name="mnuNewCollection" Click="mnuNewCollection_Click" />
      <MenuItem Foreground="DarkMagenta" Header="Images" x:Name="mnuImage">
        <MenuItem Header="Images >>>" x:Name="mnuImages" Click="mnuChange_Click" />
        <Separator />
        <MenuItem Header="Sequential Collections" x:Name="mnuSequentialCollections" IsCheckable="True" IsChecked="False" Click="mnuSequentialCollections_Click"/>
        <StackPanel Orientation="Horizontal">
          <Label x:Name="STimerLabel" Content="Sequential Timer:" HorizontalAlignment="Left" Height="28px" Margin="1" />
          <TextBox x:Name="SequentialTimer" TextWrapping="NoWrap" Width="40" Text="120" HorizontalAlignment="Left" Margin="1" Height="18px" />
          <Label x:Name="STimerLabel2" Content="Seconds" HorizontalAlignment="Left" Height="28px" Margin="1" />
        </StackPanel>
        <Separator />
        <StackPanel Orientation="Horizontal">
          <Label x:Name="ZoomLabel" Content="Zoom Factor:" HorizontalAlignment="Left" Height="28px" Margin="1" />
          <TextBox x:Name="ImagesZoom" TextWrapping="NoWrap" Width="40" Text="2.5" HorizontalAlignment="Left" Margin="1" Height="18px" />
        </StackPanel>
      </MenuItem>
      <MenuItem Foreground="DarkSlateBlue" Header="Config" x:Name="mnuConfig" Click="mnuConfig_Click"/>
      <MenuItem Foreground="Brown" Header="About  " x:Name="mnuAbout" Click="mnuAbout_Click"/>
      <MenuItem Foreground="DarkGreen" Header="Show X,Y,Z:" x:Name="mnuShowXYZ" IsCheckable="True" IsChecked="True" Click="mnuShowXYZ_Click"/>
      <MenuItem Foreground="Blue" Header="X = 180" x:Name="X_Menu"/>
      <MenuItem Foreground="DarkMagenta" Header="Y = 180" x:Name="Y_Menu"/>
      <MenuItem Foreground="DarkGreen" Header="Z = 000" x:Name="Z_Menu"/>
    </Menu>
    <ScrollBar x:Name="hscroll" DockPanel.Dock="Bottom" Orientation="Horizontal" Minimum="-180" Maximum="180" LargeChange="1" SmallChange="1" Value="-180" />
    <ScrollBar x:Name="vscroll" DockPanel.Dock="Right" Orientation="Vertical" Minimum="-180" Maximum="180" LargeChange="1" SmallChange="1" Value="-180" Width="17" />

    <DockPanel Margin="0" x:Name="dockOuter" Background="Black">
      <DockPanel Margin="0" x:Name="dockCube" Background="Black">
        <Viewport3D Margin="0" x:Name="viewCube">

          <Viewport3D.Camera>
            <PerspectiveCamera Position = "1.75, 2.75, 2.75" LookDirection = "-1.75, -2.75, -2.75" UpDirection = "0, 1, 0" FieldOfView = "60">
              <PerspectiveCamera.Transform>
                <Transform3DGroup>
                  <RotateTransform3D>
                    <RotateTransform3D.Rotation>
                      <AxisAngleRotation3D Axis="0 1 0" Angle="{Binding Value, ElementName=hscroll}" />
                    </RotateTransform3D.Rotation>
                  </RotateTransform3D>
                  <RotateTransform3D>
                    <RotateTransform3D.Rotation>
                      <AxisAngleRotation3D Axis="1 0 0" Angle="{Binding Value, ElementName=vscroll}" />
                    </RotateTransform3D.Rotation>
                  </RotateTransform3D>
                </Transform3DGroup>
              </PerspectiveCamera.Transform>
            </PerspectiveCamera>
          </Viewport3D.Camera>
          <ModelVisual3D>
            <ModelVisual3D.Content>
              <Model3DGroup>
                <!-- Lights -->
                <AmbientLight Color="Gray" />
                <DirectionalLight Color="Gray" Direction="1,-2,-3" />
                <DirectionalLight Color="Gray" Direction="-1,2,3" />

                <!-- Top -->
                <GeometryModel3D>
                  <GeometryModel3D.Geometry>
                    <MeshGeometry3D Positions = "-1,1,1 1,1,1 1,1,-1 -1,1,-1" TriangleIndices = "0 1 2     2,3,0" TextureCoordinates="0,1 1,1 1,0 0,0" />
                  </GeometryModel3D.Geometry>
                  <GeometryModel3D.Material>
                    <DiffuseMaterial>
                      <DiffuseMaterial.Brush>
                        <ImageBrush x:Name="ImgTop" ImageSource="/Images/Empty/Top.png"/>
                      </DiffuseMaterial.Brush>
                    </DiffuseMaterial>
                  </GeometryModel3D.Material>
                </GeometryModel3D>

                <!-- Front -->
                <GeometryModel3D>
                  <GeometryModel3D.Geometry>
                    <MeshGeometry3D Positions = "-1,-1,1 1,-1,1 1,1,1 -1,1,1" TriangleIndices = "0 1 2     2,3,0" TextureCoordinates="0,1 1,1 1,0 0,0" />
                  </GeometryModel3D.Geometry>
                  <GeometryModel3D.Material>
                    <DiffuseMaterial>
                      <DiffuseMaterial.Brush>
                        <ImageBrush x:Name="ImgFront" ImageSource="/Images/Empty/Front.png"/>
                      </DiffuseMaterial.Brush>
                    </DiffuseMaterial>
                  </GeometryModel3D.Material>
                </GeometryModel3D>

                <!-- Right -->
                <GeometryModel3D>
                  <GeometryModel3D.Geometry>
                    <MeshGeometry3D Positions = "1,-1,1 1,-1,-1 1,1,-1 1,1,1" TriangleIndices = "0 1 2     2,3,0" TextureCoordinates="0,1 1,1 1,0 0,0" />
                  </GeometryModel3D.Geometry>
                  <GeometryModel3D.Material>
                    <DiffuseMaterial>
                      <DiffuseMaterial.Brush>
                        <ImageBrush x:Name="ImgRight" ImageSource="/Images/Empty/Right.png"/>
                      </DiffuseMaterial.Brush>
                    </DiffuseMaterial>
                  </GeometryModel3D.Material>
                </GeometryModel3D>

                <!-- Left -->
                <GeometryModel3D>
                  <GeometryModel3D.Geometry>
                    <MeshGeometry3D Positions = "-1,-1,-1 -1,-1,1 -1,1,1 -1,1,-1" TriangleIndices = "0 1 2     2,3,0" TextureCoordinates="0,1 1,1 1,0 0,0" />
                  </GeometryModel3D.Geometry>
                  <GeometryModel3D.Material>
                    <DiffuseMaterial>
                      <DiffuseMaterial.Brush>
                        <ImageBrush x:Name="ImgLeft" ImageSource="/Images/Empty/Left.png"/>
                      </DiffuseMaterial.Brush>
                    </DiffuseMaterial>
                  </GeometryModel3D.Material>
                </GeometryModel3D>

                <!-- Back -->
                <GeometryModel3D>
                  <GeometryModel3D.Geometry>
                    <MeshGeometry3D Positions = "1,-1,-1 -1,-1,-1 -1,1,-1 1,1,-1" TriangleIndices = "0 1 2     2,3,0" TextureCoordinates="0,1 1,1 1,0 0,0" />
                  </GeometryModel3D.Geometry>
                  <GeometryModel3D.Material>
                    <DiffuseMaterial>
                      <DiffuseMaterial.Brush>
                        <ImageBrush x:Name="ImgBack" ImageSource="/Images/Empty/Back.png"/>
                      </DiffuseMaterial.Brush>
                    </DiffuseMaterial>
                  </GeometryModel3D.Material>
                </GeometryModel3D>

                <!-- Bottom -->
                <GeometryModel3D>
                  <GeometryModel3D.Geometry>
                    <MeshGeometry3D Positions = "-1,-1,-1 1,-1,-1 1,-1,1 -1,-1,1" TriangleIndices = "0 1 2     2,3,0" TextureCoordinates="0,1 1,1 1,0 0,0"
                                        />
                  </GeometryModel3D.Geometry>
                  <GeometryModel3D.Material>
                    <DiffuseMaterial>
                      <DiffuseMaterial.Brush>
                        <ImageBrush x:Name="ImgBottom" ImageSource="/Images/Empty/Bottom.png"/>
                      </DiffuseMaterial.Brush>
                    </DiffuseMaterial>
                  </GeometryModel3D.Material>
                </GeometryModel3D>

              </Model3DGroup>
            </ModelVisual3D.Content>
          </ModelVisual3D>

        </Viewport3D>
      </DockPanel>
    </DockPanel>
  </DockPanel>
</Window>

--- END OF FILE -----------------------------------------------------------------------------------------------------------------------

    #region " Menu "
    private void mnuSave_Click(Object sender, RoutedEventArgs e)
    {
      // Draw the viewport into a RenderTargetBitmap.
      RenderTargetBitmap bm = new RenderTargetBitmap((int)dockCube.ActualWidth, (int)dockCube.ActualHeight, 96, 96, PixelFormats.Pbgra32);
      bm.Render(dockCube);

      // Make a PNG encoder.
      PngBitmapEncoder encoder = new PngBitmapEncoder();
      encoder.Frames.Add(BitmapFrame.Create(bm));

      // Save the file.
      using (FileStream fs = new FileStream("Saved.png",
          FileMode.Create, FileAccess.Write, FileShare.None))
      {
        encoder.Save(fs);
      }

      System.Media.SystemSounds.Beep.Play();
    }
    private void mnuView_Click(Object sender, RoutedEventArgs e)
    {
      // Move the camera to a specific position.
      MenuItem item = sender as MenuItem;
      string txt = item.Header.ToString().Replace("(", "").Replace(")", "");
      string[] values = txt.Split(',');
      float x = 2.5f * float.Parse(values[0]);
      float y = 2.5f * float.Parse(values[1]);
      float z = 2.5f * float.Parse(values[2]);
      float yup = y > 0 ? 1 : -1;
      PositionCamera(x, y, z, yup);
    }
    private void mnuRotate_Click(object sender, RoutedEventArgs e)
    {
      MenuItem item = sender as MenuItem;
      string txt = item.Header.ToString();

      if (txt == "Rotate X")
        hAxis = mnuRotateX.IsChecked;

      if (txt == "Rotate Y")
        vAxis = mnuRotateY.IsChecked;

      if (txt == "Rotation")
      {
        RotationAutoStart();
      }
    }
    private void mnuSequentialCollections_Click(object sender, RoutedEventArgs e)
    {
      if (Conv.ToInteger(SequentialTimer.Text) < 5)
        SequentialTimer.Text = "5";

      ConfigIO.SettingsWrite("Sequential Timer Seconds", SequentialTimer.Text);
      string txt = "No";

      if (mnuSequentialCollections.IsChecked == true)
      {
        txt = "Yes";
        CollectionTimerStart();
      }
      else
      {
        CollectionTimer.Stop();
      }

      ConfigIO.SettingsWrite("Sequential Collections", txt);
    }
    private void mnuNewCollection_Click(object sender, RoutedEventArgs e)
    {
      var ImagesForm = new Images();
      ImagesForm.ShowDialog();

      LoadImages("Last Collection");
    }
    private void mnuChange_Click(object sender, RoutedEventArgs e)
    {
      MenuItem item = sender as MenuItem;
      string txt = item.Header.ToString();

      if (txt != "Images >>>")
      {
        MenuItem itemChecked = (MenuItem)sender;
        MenuItem itemParent = (MenuItem)itemChecked.Parent;

        foreach (MenuItem item2 in itemParent.Items)
        {
          item2.IsChecked = false;
        }
        ChangeImage(txt);
        item.IsChecked = true;
        ConfigIO.SettingsWrite("Last Collection", txt);

        string LastScreenState = "Normal";
        if (this.WindowState == WindowState.Maximized) LastScreenState = "Maximize";
        ConfigIO.SettingsWrite("Last Screen State", LastScreenState);
        ConfigIO.SettingsWrite("Rotation Speed", RotationSpeed.Text);
        ConfigIO.SettingsWrite("Last Zoom Factor", ImagesZoom.Text);
      }
    }
    private void mnuConfig_Click(object sender, RoutedEventArgs e)
    {
      var ConfigForm = new Config();
      ConfigForm.ShowDialog();
    }
    private void mnuAbout_Click(object sender, RoutedEventArgs e)
    {
      var AboutForm = new About();
      AboutForm.ShowDialog();
    }
    private void mnuShowXYZ_Click(object sender, RoutedEventArgs e)
    {
      if (mnuShowXYZ.IsChecked == false)
      {
        X_Menu.Header = "X = 000";
        Y_Menu.Header = "Y = 000";
        Z_Menu.Header = "Z = 0.0";
      }
    }
    #endregion

bottom of page