//------------------------------------------------------------------------------ // Title: Dance Like Me // Name: Camille Jacobson // This program utilizes code from the "SkeletonBasics-WPF" application from the // Kinect for Windows Developer Toolkit v1.8.0. which was developed by Microsoft. // I used the basic structure of the program (a WPF application in C# with a xaml file for a UI) // and the code to process the skeleton from the Kinect. I then took this code and built // Dance Like Me off of it by adding functionality and creating a way for users to store and // see dance moves with the skeleton. // I noted in the documentation where functions that were written by Microsoft stayed the same, // I had modified, and the ones I wrote on my own. (NOTE: The SkeletonBasics-WPF application // only had about 350 lines of code to begin with that I then reduced and reused within this program. // I also worked somewhat with the xaml file that accompanied the SkeletonBasics-WPF app. I tried to eliminate // what I felt useless, but found that there were certain elements that, in order for the program to run, I // had to keep. The xaml file is also documented. //------------------------------------------------------------------------------ namespace Microsoft.Samples.Kinect.SkeletonBasics { using System.Collections.Generic; using System.IO; using System.Windows; using System.Windows.Media; using Microsoft.Kinect; using System.Windows.Threading; using System; using System.Windows.Controls; public partial class MainWindow : Window { //The following variables (labeled with a "summary" tag) were created by //Microsoft in the SkeletonBasics application. I used these throughout //I did modify the JointThickness and the BodyCenterThickness /// /// Width of output drawing /// private const float RenderWidth = 640.0f; /// /// Height of our output drawing /// private const float RenderHeight = 480.0f; /// /// Thickness of drawn joint lines /// private const double JointThickness = 5; /// /// Thickness of body center ellipse /// private const double BodyCenterThickness = 15; /// /// Thickness of clip edge rectangles /// private const double ClipBoundsThickness = 10; /// /// Brush used to draw skeleton center point /// private readonly Brush centerPointBrush = Brushes.Blue; /// /// Brush used for drawing joints that are currently tracked /// private readonly Brush trackedJointBrush = new SolidColorBrush(Color.FromArgb(255, 68, 192, 68)); /// /// Brush used for drawing joints that are currently inferred /// private readonly Brush inferredJointBrush = Brushes.Yellow; /// /// Pen used for drawing bones that are currently tracked /// private readonly Pen trackedBonePen = new Pen(Brushes.Purple, 6); /// /// Pen used for drawing bones that are currently inferred /// private readonly Pen inferredBonePen = new Pen(Brushes.Gray, 1); //Now, the following variables are mine //These are all brushes that determine the color of the skeleton - depends on the button you clicked //The Brush objects are not used outside of creating the Pens. The pens are used in drawBone(). //I used a Hex to RGB color converter to get the specific color that matched each button, so they // resemble each other: https://www.rapidtables.com/convert/color/hex-to-rgb.html //In Bust A Move, the skeleton turns purple when recording private static Brush moveBrush = new SolidColorBrush(Color.FromArgb(255, 189, 44, 231)); private readonly Pen movePen = new Pen(moveBrush, 6); //In Bust A Move, the skeleton starts as orange private readonly Pen allInPen = new Pen(Brushes.Orange, 6); //In Freestyle, the skeleton is red private static Brush freestyleBrush = new SolidColorBrush(Color.FromArgb(255, 232, 43, 43)); private readonly Pen freestylePen = new Pen(freestyleBrush, 6); //In Routines, the skeleton is green private static Brush routineBrush = new SolidColorBrush(Color.FromArgb(255, 98, 215, 14)); private readonly Pen routinePen = new Pen(routineBrush, 6); //In PartnerUp, the skeleton is blue private static Brush partnerUpBrush = new SolidColorBrush(Color.FromArgb(255, 15, 190, 255)); private readonly Pen partnerUpPen = new Pen(partnerUpBrush, 6); //These variables were also created by Microsoft /// /// Active Kinect sensor /// private KinectSensor sensor; /// /// Drawing group for skeleton rendering output /// private DrawingGroup drawingGroup; /// /// Drawing image that we will display /// private DrawingImage imageSource; //The following code is used to generate the path for this file /*Code to get a general file came from the following pages (as well as assistance from Dr. McVey): * https://www.c-sharpcorner.com/UploadFile/370e35/basedirectory-vs-currentdirectory-in-C-Sharp/ * https://stackoverflow.com/questions/14899422/how-to-navigate-a-few-folders-up * */ private static string filePathLong = AppDomain.CurrentDomain.BaseDirectory; private static string filePathShort = System.IO.Path.GetFullPath(System.IO.Path.Combine(filePathLong, @"..\..\")); //steps back from .exe location /// /// Initializes a new instance of the MainWindow class. /// This function was created by Microsoft /// public MainWindow() { InitializeComponent(); } /// /// Draws indicators to show which edges are clipping skeleton data - Created by Microsoft /// Camille Jacobson added in-line documentation on this function /// /// skeleton to draw clipping information for /// drawing context to draw to /// 2/19/2019 as of this day, this method has NOT been modified /// 4/10/2019 as of this day, this method has STILL NOT been modified. I do not plan on /// changing Microsoft's detection, just when, during execution, we detect when a skeleton goes out of bounds private static void RenderClippedEdges(Skeleton skeleton, DrawingContext drawingContext) { if (skeleton.ClippedEdges.HasFlag(FrameEdges.Bottom)) //Bottom edge of screen { drawingContext.DrawRectangle( Brushes.Red, null, new Rect(0, RenderHeight - ClipBoundsThickness, RenderWidth, ClipBoundsThickness)); } if (skeleton.ClippedEdges.HasFlag(FrameEdges.Top)) //Top edge of screen { drawingContext.DrawRectangle( Brushes.Red, null, new Rect(0, 0, RenderWidth, ClipBoundsThickness)); } if (skeleton.ClippedEdges.HasFlag(FrameEdges.Left)) //Left edge of screen { drawingContext.DrawRectangle( Brushes.Red, null, new Rect(0, 0, ClipBoundsThickness, RenderHeight)); } if (skeleton.ClippedEdges.HasFlag(FrameEdges.Right)) //Right edge of screen { drawingContext.DrawRectangle( Brushes.Red, null, new Rect(RenderWidth - ClipBoundsThickness, 0, ClipBoundsThickness, RenderHeight)); } } //bools that keep track of which page I am on //Once you click on a button, it is noted that you are in that page private bool inFreestyle = false; private bool inMove = false; private bool inHomeScreen = false; private bool inRoutines = false; private bool inPartnerUp = false; /// /// Event handler for "Freestyle" button on home page. /// Disables and hides the home screen, changes inHomeScreen to false /// /// /// private void FreestyleOnClick(object sender, RoutedEventArgs e) { this.HomeButtons.Visibility = Visibility.Collapsed; this.HomeButtons.IsEnabled = false; this.FreestyleGrid.Visibility = Visibility.Visible; this.FreestyleGrid.IsEnabled = true; this.ImageViewbox.Visibility = Visibility.Collapsed; inHomeScreen = false; } /// /// On the FreestyleGrid, there's a button to start tracking the skeleton. /// Once clicked, a skeleton can be drawn to the screen. A back button appears for /// when users want to return to the Home Screen /// /// /// private void StartFreestyleBtnClick(object sender, RoutedEventArgs e) { inFreestyle = true; this.ImageViewbox.Visibility = Visibility.Visible; this.FreestyleGrid.Visibility = Visibility.Collapsed; this.FreestyleGrid.IsEnabled = false; this.BackButton.Visibility = Visibility.Visible; this.BackButton.IsEnabled = true; } /// /// Event handler for the back button used in the various modes of the game. /// Returns user to Home Screen and flips every page bool to false except inHomeScreen /// /// /// private void BackBtnClicked(object sender, RoutedEventArgs e) { if (this.InstructionsGrid.Visibility == Visibility.Visible) //Collapses instruction screen { this.InstructionsGrid.Visibility = Visibility.Collapsed; } this.BackButton.Visibility = Visibility.Collapsed; this.BackButton.IsEnabled = false; this.HomeButtons.Visibility = Visibility.Visible; this.HomeButtons.IsEnabled = true; this.ImageViewbox.Visibility = Visibility.Visible; inFreestyle = false; inRoutines = false; inMove = false; inPartnerUp = false; inHomeScreen = true; } /// /// Event handler for "Bust A Move" button on home page /// Disables and hides home screen and makes instruction grid appear /// /// /// private void MoveOnClick(object sender, RoutedEventArgs e) { this.HomeButtons.Visibility = Visibility.Collapsed; this.HomeButtons.IsEnabled = false; this.InstructionsGrid.Visibility = Visibility.Visible; this.ImageViewbox.Visibility = Visibility.Collapsed; } /// /// Start button for Bust A Move. Changes bool for inMove to true and inHomeScreen to false. /// Shows the 5 initializing buttons to start a dance move recording. /// /// /// private void StartBtnClick(object sender, RoutedEventArgs e) { inMove = true; inHomeScreen = false; this.RightHandButton.Visibility = Visibility.Visible; this.LeftHandButton.Visibility = Visibility.Visible; this.LeftFootButton.Visibility = Visibility.Visible; this.RightFootButton.Visibility = Visibility.Visible; this.HeadButton.Visibility = Visibility.Visible; this.InstructionsGrid.Visibility = Visibility.Collapsed; this.ImageViewbox.Visibility = Visibility.Visible; } /// /// Once a dance move is done being recorded, a notification screen pops up for user benefit. /// This event handler is for the "Okay!" button and returns the user to the Home Screen /// /// /// private void ReturnBtnClick(object sender, RoutedEventArgs e) { inMove = false; this.BustMoveGrid.Visibility = Visibility.Collapsed; this.BustMoveGrid.IsEnabled = false; this.HomeButtons.Visibility = Visibility.Visible; this.HomeButtons.IsEnabled = true; this.ImageViewbox.Visibility = Visibility.Visible; } /// /// This event handler is for the end screen for Routines/PartnerUp for when /// a dance routine is completed before a user kills the routine via the back button. /// /// /// private void EndRoutineBtnClick(object sender, RoutedEventArgs e) { inRoutines = false; inMove1 = false; inMove2 = false; inMove3 = false; inPartnerUp = false; this.RoutineGrid.Visibility = Visibility.Collapsed; this.RoutineGrid.IsEnabled = false; this.HomeButtons.Visibility = Visibility.Visible; this.HomeButtons.IsEnabled = true; this.ImageViewbox.Visibility = Visibility.Visible; this.BackButton.Visibility = Visibility.Collapsed; this.BackButton.IsEnabled = false; } //The following variables are utilized in RotuineOnClick, dtClockTime, PartnerUpOnClick, and //PartnerUp's respective timer handler private static SkeletonPoint[,] routine1; private static SkeletonPoint[,] routine2; private static SkeletonPoint[,] routine3; private Brush drawBrushTest = Brushes.Goldenrod; private static int move1, move2, move3; private static int lineCount1, lineCount2, lineCount3; private static DispatcherTimer dtClockTime; /// /// So, this function is the event handler for when a user clicks the Routines button. /// In this function, 3 dance moves are randomly selected and those dance moves are pulled in from /// the SavedMoves folder and placed into their respective 2D arrays (routine1, routine2, or routine3). /// A timer (dtClockTime) is also started in this function and given the event handler: dtClockTime_Tick /// This event handler is called every 10 milliseconds /// /// /// private void RoutineOnClick(object sender, RoutedEventArgs e) { /* The following random number generated is taken from the following source: * https://www.c-sharpcorner.com/article/generating-random-number-and-string-in-C-Sharp/ */ Random rnd = new Random(); //Random number generator inRoutines = true; this.HomeButtons.Visibility = Visibility.Collapsed; //Clears out HomeScreen this.HomeButtons.IsEnabled = false; this.BackButton.IsEnabled = true; //Brings in the back button this.BackButton.Visibility = Visibility.Visible; int moveAmount = System.IO.Directory.GetDirectories(System.IO.Path.Combine(filePathShort, "SavedMoves")).Length; moveAmount++; //incremented because the random generator goes between the range of x to y-1 inHomeScreen = false; //the following is the list of random dance moves - as a programmer decision, the computer //could potentially select the same three moves in a row. I will not prevent this. move1 = rnd.Next(1, moveAmount); move2 = rnd.Next(1, moveAmount); move3 = rnd.Next(1, moveAmount); //Array of file names within saved moves - order matters! string[] fileNames = {"HipCenter.txt", "ShoulderCenter.txt", "Head.txt", "ShoulderLeft.txt", "ElbowLeft.txt", "WristLeft.txt", "ShoulderRight.txt", "ElbowRight.txt", "WristRight.txt", "KneeLeft.txt", "AnkleLeft.txt", "KneeRight.txt", "AnkleRight.txt" }; //setting up the timer dtClockTime = new DispatcherTimer(); dtClockTime.Interval = new TimeSpan(0, 0, 0, 0, 10);//Yup, tick every 10 milliseconds. This reduced the seuizure-like effect of the redrawing dtClockTime.Tick += dtClockTime_Tick; //setting up to read from the files for each dance move string line; SkeletonPoint pos = new SkeletonPoint(); Joint joint = new Joint(); int lineCounter = 0; totalArrayCount = 0; intervalCount = 0; string originalFolder = System.IO.Path.Combine(filePathShort, "SavedMoves"); string subDirectory1 = System.IO.Path.Combine(originalFolder,move1.ToString()); //sub-directory for each dance move string subDirectory2 = System.IO.Path.Combine(originalFolder, move2.ToString()); string subDirectory3 = System.IO.Path.Combine(originalFolder, move3.ToString()); lineCount1 = getLineCount(move1); //gets line count for each dance move lineCount2 = getLineCount(move2); lineCount3 = getLineCount(move3); routine1 = new SkeletonPoint[13, lineCount1]; //initialize routine arrays routine2 = new SkeletonPoint[13, lineCount2]; routine3 = new SkeletonPoint[13, lineCount3]; inMove1 = true; //starting in Move1 inMove2 = false; inMove3 = false; string generalSubDirectory = subDirectory1; //this for loop will get every 2D array ready to be drawn to the screen for (int i=0; i<3; i++) //3 = number of selected dance moves { if (i == 1) //changing the directory if the move changes { generalSubDirectory = subDirectory2; } if (i == 2) { generalSubDirectory = subDirectory3; } for(int j=0; j<13; j++) //goes through the file of each joint and assigns the joint position to a spot in the array { lineCounter = 0; using (System.IO.StreamReader file = new System.IO.StreamReader(System.IO.Path.Combine(generalSubDirectory, fileNames[j]))) { while ((line = file.ReadLine()) != null) { string[] nums = line.Split(' '); //splits each line and gets it ready to be a Position pos.X = (float)System.Convert.ToDouble(nums[0]); //X Coordinate pos.Y = (float)System.Convert.ToDouble(nums[1]); //Y Coordinate pos.Z = (float)System.Convert.ToDouble(nums[2]); //Z Coordinate joint.Position = pos; if (i == 0) //depending on the routine you're focusing on, change the array { routine1[j, lineCounter] = pos; } else if (i == 1) { routine2[j, lineCounter] = pos; } else { routine3[j, lineCounter] = pos; } lineCounter++; } file.Close(); } } } dtClockTime.Start(); } private static int totalArrayCount = 0; private static int intervalCount = 0; private static bool inMove1; private static bool inMove2; private static bool inMove3; /// /// This is the event handler for dtClockTime. Every time the timer ticks (every 10 milliseconds), a new skeleton is drawn (and, /// actually, the same skeleton is drawn 5 times due to the playback speed). This function draws those skeletons. /// /// /// /// For reference, here's how the skeleton appears and explains why the lines are drawn the way they were: /// 2 /// | /// 3___1 ___6 /// | | | /// 4 | 7 /// | | | /// 5 0 8 /// | | /// | | /// 9 11 /// | | /// | | /// 10 12 /// I would've LOVED to condense the repetitive lines down, but I could not becuase of this skeleton private void dtClockTime_Tick(object sender, EventArgs e) { Brush drawBrush = Brushes.White; using (DrawingContext drawingCon = this.drawingGroup.Append()) { if (!inHomeScreen && inRoutines) //Guarantees that these skeletons won't be drawn anywhere except in Routines { if (inMove1) { //Draws ellipses for all 13 joints for (int i = 0; i < 13; i++) { drawingCon.DrawEllipse(drawBrush, null, this.SkeletonPointToScreen(routine1[i, totalArrayCount]), JointThickness, JointThickness); } //lines connecting the ellipses drawingCon.DrawLine(routinePen, SkeletonPointToScreen(routine1[1, totalArrayCount]), SkeletonPointToScreen(routine1[0, totalArrayCount])); drawingCon.DrawLine(routinePen, SkeletonPointToScreen(routine1[1, totalArrayCount]), SkeletonPointToScreen(routine1[6, totalArrayCount])); drawingCon.DrawLine(routinePen, SkeletonPointToScreen(routine1[2, totalArrayCount]), SkeletonPointToScreen(routine1[1, totalArrayCount])); drawingCon.DrawLine(routinePen, SkeletonPointToScreen(routine1[3, totalArrayCount]), SkeletonPointToScreen(routine1[1, totalArrayCount])); drawingCon.DrawLine(routinePen, SkeletonPointToScreen(routine1[4, totalArrayCount]), SkeletonPointToScreen(routine1[3, totalArrayCount])); drawingCon.DrawLine(routinePen, SkeletonPointToScreen(routine1[5, totalArrayCount]), SkeletonPointToScreen(routine1[4, totalArrayCount])); drawingCon.DrawLine(routinePen, SkeletonPointToScreen(routine1[7, totalArrayCount]), SkeletonPointToScreen(routine1[6, totalArrayCount])); drawingCon.DrawLine(routinePen, SkeletonPointToScreen(routine1[8, totalArrayCount]), SkeletonPointToScreen(routine1[7, totalArrayCount])); drawingCon.DrawLine(routinePen, SkeletonPointToScreen(routine1[0, totalArrayCount]), SkeletonPointToScreen(routine1[9, totalArrayCount])); drawingCon.DrawLine(routinePen, SkeletonPointToScreen(routine1[9, totalArrayCount]), SkeletonPointToScreen(routine1[10, totalArrayCount])); drawingCon.DrawLine(routinePen, SkeletonPointToScreen(routine1[0, totalArrayCount]), SkeletonPointToScreen(routine1[11, totalArrayCount])); drawingCon.DrawLine(routinePen, SkeletonPointToScreen(routine1[11, totalArrayCount]), SkeletonPointToScreen(routine1[12, totalArrayCount])); if (totalArrayCount == (lineCount1 - 10)) //if you make it to the end of the dance move, go the the next { totalArrayCount = 0; inMove1 = false; inMove2 = true; } } if (inMove2) { //ellipses for (int i = 0; i < 13; i++) { drawingCon.DrawEllipse(drawBrush, null, this.SkeletonPointToScreen(routine2[i, totalArrayCount]), JointThickness, JointThickness); } //lines connecting the ellipses drawingCon.DrawLine(routinePen, SkeletonPointToScreen(routine2[1, totalArrayCount]), SkeletonPointToScreen(routine2[0, totalArrayCount])); drawingCon.DrawLine(routinePen, SkeletonPointToScreen(routine2[1, totalArrayCount]), SkeletonPointToScreen(routine2[6, totalArrayCount])); drawingCon.DrawLine(routinePen, SkeletonPointToScreen(routine2[2, totalArrayCount]), SkeletonPointToScreen(routine2[1, totalArrayCount])); drawingCon.DrawLine(routinePen, SkeletonPointToScreen(routine2[3, totalArrayCount]), SkeletonPointToScreen(routine2[1, totalArrayCount])); drawingCon.DrawLine(routinePen, SkeletonPointToScreen(routine2[4, totalArrayCount]), SkeletonPointToScreen(routine2[3, totalArrayCount])); drawingCon.DrawLine(routinePen, SkeletonPointToScreen(routine2[5, totalArrayCount]), SkeletonPointToScreen(routine2[4, totalArrayCount])); drawingCon.DrawLine(routinePen, SkeletonPointToScreen(routine2[7, totalArrayCount]), SkeletonPointToScreen(routine2[6, totalArrayCount])); drawingCon.DrawLine(routinePen, SkeletonPointToScreen(routine2[8, totalArrayCount]), SkeletonPointToScreen(routine2[7, totalArrayCount])); drawingCon.DrawLine(routinePen, SkeletonPointToScreen(routine2[0, totalArrayCount]), SkeletonPointToScreen(routine2[9, totalArrayCount])); drawingCon.DrawLine(routinePen, SkeletonPointToScreen(routine2[9, totalArrayCount]), SkeletonPointToScreen(routine2[10, totalArrayCount])); drawingCon.DrawLine(routinePen, SkeletonPointToScreen(routine2[0, totalArrayCount]), SkeletonPointToScreen(routine2[11, totalArrayCount])); drawingCon.DrawLine(routinePen, SkeletonPointToScreen(routine2[11, totalArrayCount]), SkeletonPointToScreen(routine2[12, totalArrayCount])); if (totalArrayCount == (lineCount2 - 10)) //if Move2 is done, go to Move3 { totalArrayCount = 0; inMove2 = false; inMove3 = true; } } if (inMove3) { //ellipses for (int i = 0; i < 13; i++) { drawingCon.DrawEllipse(drawBrush, null, this.SkeletonPointToScreen(routine3[i, totalArrayCount]), JointThickness, JointThickness); } //lines connecting the ellipses drawingCon.DrawLine(routinePen, SkeletonPointToScreen(routine3[1, totalArrayCount]), SkeletonPointToScreen(routine3[0, totalArrayCount])); drawingCon.DrawLine(routinePen, SkeletonPointToScreen(routine3[1, totalArrayCount]), SkeletonPointToScreen(routine3[6, totalArrayCount])); drawingCon.DrawLine(routinePen, SkeletonPointToScreen(routine3[2, totalArrayCount]), SkeletonPointToScreen(routine3[1, totalArrayCount])); drawingCon.DrawLine(routinePen, SkeletonPointToScreen(routine3[3, totalArrayCount]), SkeletonPointToScreen(routine3[1, totalArrayCount])); drawingCon.DrawLine(routinePen, SkeletonPointToScreen(routine3[4, totalArrayCount]), SkeletonPointToScreen(routine3[3, totalArrayCount])); drawingCon.DrawLine(routinePen, SkeletonPointToScreen(routine3[5, totalArrayCount]), SkeletonPointToScreen(routine3[4, totalArrayCount])); drawingCon.DrawLine(routinePen, SkeletonPointToScreen(routine3[7, totalArrayCount]), SkeletonPointToScreen(routine3[6, totalArrayCount])); drawingCon.DrawLine(routinePen, SkeletonPointToScreen(routine3[8, totalArrayCount]), SkeletonPointToScreen(routine3[7, totalArrayCount])); drawingCon.DrawLine(routinePen, SkeletonPointToScreen(routine3[0, totalArrayCount]), SkeletonPointToScreen(routine3[9, totalArrayCount])); drawingCon.DrawLine(routinePen, SkeletonPointToScreen(routine3[9, totalArrayCount]), SkeletonPointToScreen(routine3[10, totalArrayCount])); drawingCon.DrawLine(routinePen, SkeletonPointToScreen(routine3[0, totalArrayCount]), SkeletonPointToScreen(routine3[11, totalArrayCount])); drawingCon.DrawLine(routinePen, SkeletonPointToScreen(routine3[11, totalArrayCount]), SkeletonPointToScreen(routine3[12, totalArrayCount])); if (totalArrayCount == (lineCount3 - 10)) //if Move3 is done, the whole routine is done { totalArrayCount = 0; inMove3 = false; this.RoutineGrid.IsEnabled = true; this.RoutineGrid.Visibility = Visibility.Visible; this.ImageViewbox.Visibility = Visibility.Collapsed; dtClockTime.Stop(); } } } } //intervalCount is used to reduce the strobe-light effect of redrawing skeletons at the speeds they were obtained at //intervalCount is responsible for the same skeleton being drawn 5 times to a screen before moving on if (intervalCount ==5) { totalArrayCount++; //We don't move onto the next position until intervalCount hits 5 intervalCount = 0; } else { intervalCount++; } } /// /// This function iterates through a file for a given move and reports the number of lines in a file /// /// Send the number of the dance move /// Returns the total line count private int getLineCount(int moveNumber) { int lineCounter = 0; string line; string originalFolder = System.IO.Path.Combine(filePathShort, "SavedMoves"); string subDirectory1 = System.IO.Path.Combine(originalFolder, moveNumber.ToString()); using (System.IO.StreamReader file = new System.IO.StreamReader(System.IO.Path.Combine(subDirectory1, "Head.txt"))) { while ((line = file.ReadLine()) != null) { lineCounter++; //gets the amount of lines in one file, which will be the same amount for all files for this move } file.Close(); } return lineCounter; } //partnerUp1 and partnerUpA are partners and so on private static SkeletonPoint[,] partnerUp1; private static SkeletonPoint[,] partnerUp2; private static SkeletonPoint[,] partnerUp3; private static SkeletonPoint[,] partnerUpA; private static SkeletonPoint[,] partnerUpB; private static SkeletonPoint[,] partnerUpC; private static DispatcherTimer dtClockTimePartner; /// /// Similar to RoutineOnClick, this function is generating the dance routine for PartnerUp /// /// /// private void PartnerBtnClick(object sender, RoutedEventArgs e) { Random rnd = new Random(); inPartnerUp = true; this.HomeButtons.Visibility = Visibility.Collapsed; this.HomeButtons.IsEnabled = false; this.BackButton.IsEnabled = true; this.BackButton.Visibility = Visibility.Visible; int moveAmount = System.IO.Directory.GetDirectories(System.IO.Path.Combine(filePathShort, "SavedMoves")).Length; moveAmount++; //incremented because the random generator goes between the range of x to y-1 inHomeScreen = false; //the following is the list of random dance moves - as a programmer decision, the computer //could potentially select the same three moves in a row. I will not prevent this. move1 = rnd.Next(1, moveAmount); move2 = rnd.Next(1, moveAmount); move3 = rnd.Next(1, moveAmount); //Array of file names within saved moves - Order matters! //Note: There's two arrays because the 2 skeletons will be mirrored, so the order varies slightly string[] fileNames = {"HipCenter.txt", "ShoulderCenter.txt", "Head.txt", "ShoulderLeft.txt", "ElbowLeft.txt", "WristLeft.txt", "ShoulderRight.txt", "ElbowRight.txt", "WristRight.txt", "KneeLeft.txt", "AnkleLeft.txt", "KneeRight.txt", "AnkleRight.txt" }; string[] fileNamesForRightPartner = {"HipCenter.txt", "ShoulderCenter.txt", "Head.txt", "ShoulderRight.txt", "ElbowRight.txt", "WristRight.txt", "ShoulderLeft.txt", "ElbowLeft.txt", "WristLeft.txt", "KneeRight.txt", "AnkleRight.txt", "KneeLeft.txt", "AnkleLeft.txt" }; dtClockTimePartner = new DispatcherTimer(); dtClockTimePartner.Interval = new TimeSpan(0, 0, 0, 0, 10); dtClockTimePartner.Tick += dtClockTime_TickPartner; string line; SkeletonPoint pos = new SkeletonPoint(); Joint joint = new Joint(); int lineCounter = 0; arrayIndex = 0; intervalArrayIndex = 0; string originalFolder = System.IO.Path.Combine(filePathShort, "SavedMoves"); string subDirectory1 = System.IO.Path.Combine(originalFolder, move1.ToString()); string subDirectory2 = System.IO.Path.Combine(originalFolder, move2.ToString()); string subDirectory3 = System.IO.Path.Combine(originalFolder, move3.ToString()); lineCount1 = getLineCount(move1); lineCount2 = getLineCount(move2); lineCount3 = getLineCount(move3); //6 2D arrays to account for the 6 total skeletons partnerUp1 = new SkeletonPoint[13, lineCount1]; partnerUp2 = new SkeletonPoint[13, lineCount2]; partnerUp3 = new SkeletonPoint[13, lineCount3]; partnerUpA = new SkeletonPoint[13, lineCount1]; partnerUpB = new SkeletonPoint[13, lineCount2]; partnerUpC = new SkeletonPoint[13, lineCount3]; inMove1 = true; inMove2 = false; inMove3 = false; string generalSubDirectory = subDirectory1; for (int i = 0; i < 3; i++) { if (i == 1) { generalSubDirectory = subDirectory2; } if (i == 2) { generalSubDirectory = subDirectory3; } for (int j = 0; j < 13; j++) { lineCounter = 0; //accounts for the left skeleton using (System.IO.StreamReader file = new System.IO.StreamReader(System.IO.Path.Combine(generalSubDirectory, fileNames[j]))) { while ((line = file.ReadLine()) != null) { string[] nums = line.Split(' '); pos.X = (float)System.Convert.ToDouble(nums[0]); pos.Y = (float)System.Convert.ToDouble(nums[1]); pos.Z = (float)System.Convert.ToDouble(nums[2]); joint.Position = pos; pos.X = (float)(pos.X - 0.50); //accounts for shifting the skeleton leftwards on screen if (i == 0) { partnerUp1[j, lineCounter] = pos; } else if (i == 1) { partnerUp2[j, lineCounter] = pos; } else { partnerUp3[j, lineCounter] = pos; } lineCounter++; } file.Close(); } lineCounter = 0; //accounts for the right skeleton using (System.IO.StreamReader file = new System.IO.StreamReader(System.IO.Path.Combine(generalSubDirectory, fileNamesForRightPartner[j]))) { while ((line = file.ReadLine()) != null) { string[] nums = line.Split(' '); pos.X = (float)System.Convert.ToDouble(nums[0]); pos.Y = (float)System.Convert.ToDouble(nums[1]); pos.Z = (float)System.Convert.ToDouble(nums[2]); joint.Position = pos; pos.X = (float)((pos.X * -1) + 0.5); //flips the x coordinate for mirror effect and shifts it right if (i == 0) { partnerUpA[j, lineCounter] = pos; } else if (i == 1) { partnerUpB[j, lineCounter] = pos; } else { partnerUpC[j, lineCounter] = pos; } lineCounter++; } file.Close(); } } } dtClockTimePartner.Start(); } /// /// Similar to dtClockTime_Tick, except to draw both partners for PartnerUp /// /// /// /// private static int arrayIndex=0; private static int intervalArrayIndex = 0; private void dtClockTime_TickPartner(object sender, EventArgs e) { Brush drawBrush = Brushes.GreenYellow; using (DrawingContext drawingCon = this.drawingGroup.Append()) { if (!inHomeScreen && inPartnerUp) { if (inMove1) { //ellipses for (int i = 0; i < 13; i++) { drawingCon.DrawEllipse(drawBrush, null, this.SkeletonPointToScreen(partnerUp1[i, arrayIndex]), JointThickness, JointThickness); drawingCon.DrawEllipse(drawBrush, null, this.SkeletonPointToScreen(partnerUpA[i, arrayIndex]), JointThickness, JointThickness); } //lines connecting the ellipses drawingCon.DrawLine(partnerUpPen, SkeletonPointToScreen(partnerUp1[1, arrayIndex]), SkeletonPointToScreen(partnerUp1[0, arrayIndex])); drawingCon.DrawLine(partnerUpPen, SkeletonPointToScreen(partnerUp1[1, arrayIndex]), SkeletonPointToScreen(partnerUp1[6, arrayIndex])); drawingCon.DrawLine(partnerUpPen, SkeletonPointToScreen(partnerUp1[2, arrayIndex]), SkeletonPointToScreen(partnerUp1[1, arrayIndex])); drawingCon.DrawLine(partnerUpPen, SkeletonPointToScreen(partnerUp1[3, arrayIndex]), SkeletonPointToScreen(partnerUp1[1, arrayIndex])); drawingCon.DrawLine(partnerUpPen, SkeletonPointToScreen(partnerUp1[4, arrayIndex]), SkeletonPointToScreen(partnerUp1[3, arrayIndex])); drawingCon.DrawLine(partnerUpPen, SkeletonPointToScreen(partnerUp1[5, arrayIndex]), SkeletonPointToScreen(partnerUp1[4, arrayIndex])); drawingCon.DrawLine(partnerUpPen, SkeletonPointToScreen(partnerUp1[7, arrayIndex]), SkeletonPointToScreen(partnerUp1[6, arrayIndex])); drawingCon.DrawLine(partnerUpPen, SkeletonPointToScreen(partnerUp1[8, arrayIndex]), SkeletonPointToScreen(partnerUp1[7, arrayIndex])); drawingCon.DrawLine(partnerUpPen, SkeletonPointToScreen(partnerUp1[0, arrayIndex]), SkeletonPointToScreen(partnerUp1[9, arrayIndex])); drawingCon.DrawLine(partnerUpPen, SkeletonPointToScreen(partnerUp1[9, arrayIndex]), SkeletonPointToScreen(partnerUp1[10, arrayIndex])); drawingCon.DrawLine(partnerUpPen, SkeletonPointToScreen(partnerUp1[0, arrayIndex]), SkeletonPointToScreen(partnerUp1[11, arrayIndex])); drawingCon.DrawLine(partnerUpPen, SkeletonPointToScreen(partnerUp1[11, arrayIndex]), SkeletonPointToScreen(partnerUp1[12, arrayIndex])); //lines connecting the ellipses drawingCon.DrawLine(partnerUpPen, SkeletonPointToScreen(partnerUpA[1, arrayIndex]), SkeletonPointToScreen(partnerUpA[0, arrayIndex])); drawingCon.DrawLine(partnerUpPen, SkeletonPointToScreen(partnerUpA[1, arrayIndex]), SkeletonPointToScreen(partnerUpA[6, arrayIndex])); drawingCon.DrawLine(partnerUpPen, SkeletonPointToScreen(partnerUpA[2, arrayIndex]), SkeletonPointToScreen(partnerUpA[1, arrayIndex])); drawingCon.DrawLine(partnerUpPen, SkeletonPointToScreen(partnerUpA[3, arrayIndex]), SkeletonPointToScreen(partnerUpA[1, arrayIndex])); drawingCon.DrawLine(partnerUpPen, SkeletonPointToScreen(partnerUpA[4, arrayIndex]), SkeletonPointToScreen(partnerUpA[3, arrayIndex])); drawingCon.DrawLine(partnerUpPen, SkeletonPointToScreen(partnerUpA[5, arrayIndex]), SkeletonPointToScreen(partnerUpA[4, arrayIndex])); drawingCon.DrawLine(partnerUpPen, SkeletonPointToScreen(partnerUpA[7, arrayIndex]), SkeletonPointToScreen(partnerUpA[6, arrayIndex])); drawingCon.DrawLine(partnerUpPen, SkeletonPointToScreen(partnerUpA[8, arrayIndex]), SkeletonPointToScreen(partnerUpA[7, arrayIndex])); drawingCon.DrawLine(partnerUpPen, SkeletonPointToScreen(partnerUpA[0, arrayIndex]), SkeletonPointToScreen(partnerUpA[9, arrayIndex])); drawingCon.DrawLine(partnerUpPen, SkeletonPointToScreen(partnerUpA[9, arrayIndex]), SkeletonPointToScreen(partnerUpA[10, arrayIndex])); drawingCon.DrawLine(partnerUpPen, SkeletonPointToScreen(partnerUpA[0, arrayIndex]), SkeletonPointToScreen(partnerUpA[11, arrayIndex])); drawingCon.DrawLine(partnerUpPen, SkeletonPointToScreen(partnerUpA[11, arrayIndex]), SkeletonPointToScreen(partnerUpA[12, arrayIndex])); if (arrayIndex == (lineCount1 - 10)) { arrayIndex = 0; inMove1 = false; inMove2 = true; } } if (inMove2) { //ellipses for (int i = 0; i < 13; i++) { drawingCon.DrawEllipse(drawBrush, null, this.SkeletonPointToScreen(partnerUp2[i, arrayIndex]), JointThickness, JointThickness); drawingCon.DrawEllipse(drawBrush, null, this.SkeletonPointToScreen(partnerUpB[i, arrayIndex]), JointThickness, JointThickness); } //lines connecting the ellipses drawingCon.DrawLine(partnerUpPen, SkeletonPointToScreen(partnerUp2[1, arrayIndex]), SkeletonPointToScreen(partnerUp2[0, arrayIndex])); drawingCon.DrawLine(partnerUpPen, SkeletonPointToScreen(partnerUp2[1, arrayIndex]), SkeletonPointToScreen(partnerUp2[6, arrayIndex])); drawingCon.DrawLine(partnerUpPen, SkeletonPointToScreen(partnerUp2[2, arrayIndex]), SkeletonPointToScreen(partnerUp2[1, arrayIndex])); drawingCon.DrawLine(partnerUpPen, SkeletonPointToScreen(partnerUp2[3, arrayIndex]), SkeletonPointToScreen(partnerUp2[1, arrayIndex])); drawingCon.DrawLine(partnerUpPen, SkeletonPointToScreen(partnerUp2[4, arrayIndex]), SkeletonPointToScreen(partnerUp2[3, arrayIndex])); drawingCon.DrawLine(partnerUpPen, SkeletonPointToScreen(partnerUp2[5, arrayIndex]), SkeletonPointToScreen(partnerUp2[4, arrayIndex])); drawingCon.DrawLine(partnerUpPen, SkeletonPointToScreen(partnerUp2[7, arrayIndex]), SkeletonPointToScreen(partnerUp2[6, arrayIndex])); drawingCon.DrawLine(partnerUpPen, SkeletonPointToScreen(partnerUp2[8, arrayIndex]), SkeletonPointToScreen(partnerUp2[7, arrayIndex])); drawingCon.DrawLine(partnerUpPen, SkeletonPointToScreen(partnerUp2[0, arrayIndex]), SkeletonPointToScreen(partnerUp2[9, arrayIndex])); drawingCon.DrawLine(partnerUpPen, SkeletonPointToScreen(partnerUp2[9, arrayIndex]), SkeletonPointToScreen(partnerUp2[10, arrayIndex])); drawingCon.DrawLine(partnerUpPen, SkeletonPointToScreen(partnerUp2[0, arrayIndex]), SkeletonPointToScreen(partnerUp2[11, arrayIndex])); drawingCon.DrawLine(partnerUpPen, SkeletonPointToScreen(partnerUp2[11, arrayIndex]), SkeletonPointToScreen(partnerUp2[12, arrayIndex])); //lines connecting the ellipses drawingCon.DrawLine(partnerUpPen, SkeletonPointToScreen(partnerUpB[1, arrayIndex]), SkeletonPointToScreen(partnerUpB[0, arrayIndex])); drawingCon.DrawLine(partnerUpPen, SkeletonPointToScreen(partnerUpB[1, arrayIndex]), SkeletonPointToScreen(partnerUpB[6, arrayIndex])); drawingCon.DrawLine(partnerUpPen, SkeletonPointToScreen(partnerUpB[2, arrayIndex]), SkeletonPointToScreen(partnerUpB[1, arrayIndex])); drawingCon.DrawLine(partnerUpPen, SkeletonPointToScreen(partnerUpB[3, arrayIndex]), SkeletonPointToScreen(partnerUpB[1, arrayIndex])); drawingCon.DrawLine(partnerUpPen, SkeletonPointToScreen(partnerUpB[4, arrayIndex]), SkeletonPointToScreen(partnerUpB[3, arrayIndex])); drawingCon.DrawLine(partnerUpPen, SkeletonPointToScreen(partnerUpB[5, arrayIndex]), SkeletonPointToScreen(partnerUpB[4, arrayIndex])); drawingCon.DrawLine(partnerUpPen, SkeletonPointToScreen(partnerUpB[7, arrayIndex]), SkeletonPointToScreen(partnerUpB[6, arrayIndex])); drawingCon.DrawLine(partnerUpPen, SkeletonPointToScreen(partnerUpB[8, arrayIndex]), SkeletonPointToScreen(partnerUpB[7, arrayIndex])); drawingCon.DrawLine(partnerUpPen, SkeletonPointToScreen(partnerUpB[0, arrayIndex]), SkeletonPointToScreen(partnerUpB[9, arrayIndex])); drawingCon.DrawLine(partnerUpPen, SkeletonPointToScreen(partnerUpB[9, arrayIndex]), SkeletonPointToScreen(partnerUpB[10, arrayIndex])); drawingCon.DrawLine(partnerUpPen, SkeletonPointToScreen(partnerUpB[0, arrayIndex]), SkeletonPointToScreen(partnerUpB[11, arrayIndex])); drawingCon.DrawLine(partnerUpPen, SkeletonPointToScreen(partnerUpB[11, arrayIndex]), SkeletonPointToScreen(partnerUpB[12, arrayIndex])); if (arrayIndex == (lineCount2 - 10)) { arrayIndex = 0; inMove2 = false; inMove3 = true; } } if (inMove3) { //ellipses for (int i = 0; i < 13; i++) { drawingCon.DrawEllipse(drawBrush, null, this.SkeletonPointToScreen(partnerUp3[i, arrayIndex]), JointThickness, JointThickness); drawingCon.DrawEllipse(drawBrush, null, this.SkeletonPointToScreen(partnerUpC[i, arrayIndex]), JointThickness, JointThickness); } //lines connecting the ellipses drawingCon.DrawLine(partnerUpPen, SkeletonPointToScreen(partnerUp3[1, arrayIndex]), SkeletonPointToScreen(partnerUp3[0, arrayIndex])); drawingCon.DrawLine(partnerUpPen, SkeletonPointToScreen(partnerUp3[1, arrayIndex]), SkeletonPointToScreen(partnerUp3[6, arrayIndex])); drawingCon.DrawLine(partnerUpPen, SkeletonPointToScreen(partnerUp3[2, arrayIndex]), SkeletonPointToScreen(partnerUp3[1, arrayIndex])); drawingCon.DrawLine(partnerUpPen, SkeletonPointToScreen(partnerUp3[3, arrayIndex]), SkeletonPointToScreen(partnerUp3[1, arrayIndex])); drawingCon.DrawLine(partnerUpPen, SkeletonPointToScreen(partnerUp3[4, arrayIndex]), SkeletonPointToScreen(partnerUp3[3, arrayIndex])); drawingCon.DrawLine(partnerUpPen, SkeletonPointToScreen(partnerUp3[5, arrayIndex]), SkeletonPointToScreen(partnerUp3[4, arrayIndex])); drawingCon.DrawLine(partnerUpPen, SkeletonPointToScreen(partnerUp3[7, arrayIndex]), SkeletonPointToScreen(partnerUp3[6, arrayIndex])); drawingCon.DrawLine(partnerUpPen, SkeletonPointToScreen(partnerUp3[8, arrayIndex]), SkeletonPointToScreen(partnerUp3[7, arrayIndex])); drawingCon.DrawLine(partnerUpPen, SkeletonPointToScreen(partnerUp3[0, arrayIndex]), SkeletonPointToScreen(partnerUp3[9, arrayIndex])); drawingCon.DrawLine(partnerUpPen, SkeletonPointToScreen(partnerUp3[9, arrayIndex]), SkeletonPointToScreen(partnerUp3[10, arrayIndex])); drawingCon.DrawLine(partnerUpPen, SkeletonPointToScreen(partnerUp3[0, arrayIndex]), SkeletonPointToScreen(partnerUp3[11, arrayIndex])); drawingCon.DrawLine(partnerUpPen, SkeletonPointToScreen(partnerUp3[11, arrayIndex]), SkeletonPointToScreen(partnerUp3[12, arrayIndex])); //lines connecting the ellipses drawingCon.DrawLine(partnerUpPen, SkeletonPointToScreen(partnerUpC[1, arrayIndex]), SkeletonPointToScreen(partnerUpC[0, arrayIndex])); drawingCon.DrawLine(partnerUpPen, SkeletonPointToScreen(partnerUpC[1, arrayIndex]), SkeletonPointToScreen(partnerUpC[6, arrayIndex])); drawingCon.DrawLine(partnerUpPen, SkeletonPointToScreen(partnerUpC[2, arrayIndex]), SkeletonPointToScreen(partnerUpC[1, arrayIndex])); drawingCon.DrawLine(partnerUpPen, SkeletonPointToScreen(partnerUpC[3, arrayIndex]), SkeletonPointToScreen(partnerUpC[1, arrayIndex])); drawingCon.DrawLine(partnerUpPen, SkeletonPointToScreen(partnerUpC[4, arrayIndex]), SkeletonPointToScreen(partnerUpC[3, arrayIndex])); drawingCon.DrawLine(partnerUpPen, SkeletonPointToScreen(partnerUpC[5, arrayIndex]), SkeletonPointToScreen(partnerUpC[4, arrayIndex])); drawingCon.DrawLine(partnerUpPen, SkeletonPointToScreen(partnerUpC[7, arrayIndex]), SkeletonPointToScreen(partnerUpC[6, arrayIndex])); drawingCon.DrawLine(partnerUpPen, SkeletonPointToScreen(partnerUpC[8, arrayIndex]), SkeletonPointToScreen(partnerUpC[7, arrayIndex])); drawingCon.DrawLine(partnerUpPen, SkeletonPointToScreen(partnerUpC[0, arrayIndex]), SkeletonPointToScreen(partnerUpC[9, arrayIndex])); drawingCon.DrawLine(partnerUpPen, SkeletonPointToScreen(partnerUpC[9, arrayIndex]), SkeletonPointToScreen(partnerUpC[10, arrayIndex])); drawingCon.DrawLine(partnerUpPen, SkeletonPointToScreen(partnerUpC[0, arrayIndex]), SkeletonPointToScreen(partnerUpC[11, arrayIndex])); drawingCon.DrawLine(partnerUpPen, SkeletonPointToScreen(partnerUpC[11, arrayIndex]), SkeletonPointToScreen(partnerUpC[12, arrayIndex])); if (arrayIndex == (lineCount3 - 10)) { arrayIndex = 0; inMove3 = false; this.RoutineGrid.IsEnabled = true; this.RoutineGrid.Visibility = Visibility.Visible; this.ImageViewbox.Visibility = Visibility.Collapsed; this.EndRoutineBtn.Background = partnerUpBrush; dtClockTimePartner.Stop(); } } } } if (intervalArrayIndex == 5) { arrayIndex++; intervalArrayIndex = 0; } else { intervalArrayIndex++; } } //list of all of the joints I will NOT be tracking private List excludedJoints = new List(); /// /// Execute startup tasks /// /// object sending the event /// event arguments /// 2/19/2019 as of this date, this function has not been modified /// 3/4/2019 as of this date, this function HAS been modified to include adding joints to ecludedJoints() /// 4/11/2019 as of this date, this function has been modified to include an error message if the Kinect is unplugged /// when the application is run AND an event handler was added to the KinectSensor Status, so if the Kinect's status /// changes during runtime, the program closes (such as if it's unplugged during execution) private void WindowLoaded(object sender, RoutedEventArgs e) { // Create the DrawingGroup will be used for drawing this.drawingGroup = new DrawingGroup(); // Create an ImageSource that can be used in our image control this.imageSource = new DrawingImage(this.drawingGroup); // Display the drawing using the ImageSource Image.Source = this.imageSource; //Sets up the excludedJoints list, which are all the joints that will not be drawn //This will keep these joints from being drawn excludedJoints.Add(JointType.FootLeft); excludedJoints.Add(JointType.FootRight); excludedJoints.Add(JointType.Spine); excludedJoints.Add(JointType.HandLeft); excludedJoints.Add(JointType.HandRight); excludedJoints.Add(JointType.HipLeft); excludedJoints.Add(JointType.HipRight); // Look through all sensors and start the first connected one. // This requires that a Kinect is connected at the time of app startup. foreach (var potentialSensor in KinectSensor.KinectSensors) { if (potentialSensor.Status == KinectStatus.Connected) { this.sensor = potentialSensor; break; } } //Event handler added to detect if the status of the sensor changes during runtime KinectSensor.KinectSensors.StatusChanged += KinectSensors_StatusChanged; if (null != this.sensor) { // Turns on the skeleton stream to receive skeleton frames this.sensor.SkeletonStream.Enable(); // Adds an event handler to be called whenever there is new color frame data this.sensor.SkeletonFrameReady += this.SensorSkeletonFrameReady; // Starts the sensor try { this.sensor.Start(); } catch (IOException) { this.sensor = null; } } if (null == this.sensor) //if the sensor is NOT detected { this.ErrorGrid.Visibility = Visibility.Visible; this.HomeButtons.Visibility = Visibility.Collapsed; this.HomeButtons.IsEnabled = false; } } /// /// Built-in event handler for the KinectSensor status to throw an exception if /// the status of the Kinect changes during runtime /// Added by Camille Jacobson /// /// /// private void KinectSensors_StatusChanged(object sender, StatusChangedEventArgs e) { throw new NotImplementedException(); } /// /// Execute shutdown tasks /// /// object sending the event /// event arguments /// 2/19/2019 as of this date, this function has not been modified private void WindowClosing(object sender, System.ComponentModel.CancelEventArgs e) { if (null != this.sensor) { this.sensor.Stop(); } } /// /// Event handler for Kinect sensor's SkeletonFrameReady event /// /// object sending the event /// event arguments /// What I Have Changed As of 2/19/2019: /// -Added a new Skeleton object (skeleton) /// -Called drawOwnSkel with that new Skeleton object /// -Added a second condition to "if" statement in the "foreach" loop /// to check for "inFreestyle" ///What I Changed As of 4/11/2019 ///- Moved the call to RenderClippedEdges() to prevent these from being detected once a user steps out of bounds ///when the skeleton is even being drawn to screen (since skeletons are still being detected despite not being drawn) private void SensorSkeletonFrameReady(object sender, SkeletonFrameReadyEventArgs e) { Skeleton[] skeletons = new Skeleton[0]; using (SkeletonFrame skeletonFrame = e.OpenSkeletonFrame()) { if (skeletonFrame != null) { skeletons = new Skeleton[skeletonFrame.SkeletonArrayLength]; skeletonFrame.CopySkeletonDataTo(skeletons); } } using (DrawingContext dc = this.drawingGroup.Open()) { // Draw a transparent background to set the render size dc.DrawRectangle(Brushes.Black, null, new Rect(0.0, 0.0, RenderWidth, RenderHeight)); if (skeletons.Length != 0) { foreach (Skeleton skel in skeletons) { if (skel.TrackingState == SkeletonTrackingState.Tracked && (inFreestyle == true || inMove == true)) { RenderClippedEdges(skel, dc); this.DrawBonesAndJoints(skel, dc); } else if (skel.TrackingState == SkeletonTrackingState.PositionOnly) { dc.DrawEllipse( this.centerPointBrush, null, this.SkeletonPointToScreen(skel.Position), BodyCenterThickness, BodyCenterThickness); } } } // prevent drawing outside of the render area this.drawingGroup.ClipGeometry = new RectangleGeometry(new Rect(0.0, 0.0, RenderWidth, RenderHeight)); } } //These variables are used for Bust A Move and InitializeMoveMaking() private bool allIn = false; private bool leftWIn = false; private bool rightWIn = false; private bool leftFIn = false; private bool rightFIn = false; private bool headIn = false; /// /// Draws a skeleton's bones and joints (Included by Microsoft, modified by Camille Jacobson) /// /// skeleton to draw /// drawing context to draw to /// What I Have Changed As of 2/19/2019: /// -Commented out hand drawings /// -Tested code at end of function /// What I Have Changed As of 4/14/2019 /// -Eliminated calls to DrawBone() that would've drawn excludedJoints /// -Eliminated calls to DrawEllipse() that would've drawn excludedJoints /// -Added a check to see if inMove so that it can call InitializeMoveMaking() /// every time a skeleton is drawn private void DrawBonesAndJoints(Skeleton skeleton, DrawingContext drawingContext) { // Torso this.DrawBone(skeleton, drawingContext, JointType.Head, JointType.ShoulderCenter); this.DrawBone(skeleton, drawingContext, JointType.ShoulderCenter, JointType.ShoulderLeft); this.DrawBone(skeleton, drawingContext, JointType.ShoulderCenter, JointType.ShoulderRight); this.DrawBone(skeleton, drawingContext, JointType.ShoulderCenter, JointType.HipCenter); // Left Arm this.DrawBone(skeleton, drawingContext, JointType.ShoulderLeft, JointType.ElbowLeft); this.DrawBone(skeleton, drawingContext, JointType.ElbowLeft, JointType.WristLeft); // Right Arm this.DrawBone(skeleton, drawingContext, JointType.ShoulderRight, JointType.ElbowRight); this.DrawBone(skeleton, drawingContext, JointType.ElbowRight, JointType.WristRight); // Left Leg this.DrawBone(skeleton, drawingContext, JointType.HipCenter, JointType.KneeLeft); this.DrawBone(skeleton, drawingContext, JointType.KneeLeft, JointType.AnkleLeft); // Right Leg this.DrawBone(skeleton, drawingContext, JointType.HipCenter, JointType.KneeRight); this.DrawBone(skeleton, drawingContext, JointType.KneeRight, JointType.AnkleRight); // Render Joints foreach (Joint joint in skeleton.Joints) { Brush drawBrush = null; if (joint.TrackingState == JointTrackingState.Tracked) { foreach(JointType jointType in excludedJoints) { if (!excludedJoints.Contains(joint.JointType)) { drawBrush = this.trackedJointBrush; } } } //Inferred joints are the Kinect guessing where that joint is if it cannot immediately tell //I allow joints to still be drawn in this way as a guide to the user as to why their movements might // look odd on the playback else if (joint.TrackingState == JointTrackingState.Inferred) { foreach (JointType jointType in excludedJoints) { if (!excludedJoints.Contains(joint.JointType)) { drawBrush = this.inferredJointBrush; } } } if (drawBrush != null) { drawingContext.DrawEllipse(drawBrush, null, this.SkeletonPointToScreen(joint.Position), JointThickness, JointThickness); } } // if inMove, test to see if the skeleton is lined up with the correct circles if (inMove) { InitializeMoveMaking(skeleton, drawingContext); } } private int MoveCount; /// /// Writes the data stored in each list in the array skelPoints /// to its respective file. /// NOTE: The file names are matched up with the order in which the JointType is enumerated, so /// files and their respective list in the array match up. /// /// Array of lists for each tracked JointType in the skeleton /// private void WritingJointsToFile(List[] skelPoints, DrawingContext drawingContext) { /*Code for creating directories and files found on: * https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/file-system/how-to-create-a-file-or-folder * */ MoveCount = System.IO.Directory.GetDirectories(System.IO.Path.Combine(filePathShort, "SavedMoves")).Length; MoveCount++; string originalFolder = System.IO.Path.Combine(filePathShort, "SavedMoves"); string newSubDirectory = System.IO.Path.Combine(originalFolder, MoveCount.ToString()); string savedDirectory = System.IO.Path.Combine(System.IO.Path.Combine(filePathShort, "SavedMoves"),MoveCount.ToString()); System.IO.Directory.CreateDirectory(newSubDirectory); string[] fileNames = {"HipCenter.txt", "ShoulderCenter.txt", "Head.txt", "ShoulderLeft.txt", "ElbowLeft.txt", "WristLeft.txt", "ShoulderRight.txt", "ElbowRight.txt", "WristRight.txt", "KneeLeft.txt", "AnkleLeft.txt", "KneeRight.txt", "AnkleRight.txt" }; newSubDirectory = savedDirectory; for (int j=0; j<13; j++) { using (System.IO.StreamWriter file = new System.IO.StreamWriter(System.IO.Path.Combine(savedDirectory, fileNames[j]), true)) { foreach (SkeletonPoint skelPoint in skelPoints[j]) { file.WriteLine(skelPoint.X + " " + skelPoint.Y + " " + skelPoint.Z + " "); } } newSubDirectory = savedDirectory; } //get rid of the 5 circles and set up the message telling the user their dance move was recorded this.LeftHandButton.Visibility = Visibility.Collapsed; this.LeftHandButton.IsEnabled = false; this.RightHandButton.Visibility = Visibility.Collapsed; this.RightHandButton.IsEnabled = false; this.RightFootButton.Visibility = Visibility.Collapsed; this.RightFootButton.IsEnabled = false; this.HeadButton.Visibility = Visibility.Collapsed; this.HeadButton.IsEnabled = false; this.LeftFootButton.Visibility = Visibility.Collapsed; this.LeftFootButton.IsEnabled = false; this.BustMoveGrid.Visibility = Visibility.Visible; this.BustMoveGrid.IsEnabled = true; this.ImageViewbox.Visibility = Visibility.Collapsed; } private static int activatedCount = 0; private static int frameCount = 0; //Lists of Positions of each JointType, will be filled in InitializeMoveMaking() private static List leftWristPoints = new List(); private static List rightWristPoints = new List(); private static List hipCenterPoints = new List(); private static List headPoints = new List(); private static List leftAnklePoints = new List(); private static List rightAnklePoints = new List(); private static List leftElbowPoints = new List(); private static List rightElbowPoints = new List(); private static List leftShoulderPoints = new List(); private static List rightShoulderPoints = new List(); private static List shoulderCenterPoints = new List(); private static List leftKneePoints = new List(); private static List rightKneePoints = new List(); private static List[] allPoints = new List[13]; /// /// This function checks to see if a user is standing in the correct spot for the correct amount of time. /// If they are, the data for each joint is recorded (10 frames per second) and stored in a List. /// These lists are then placed in the List Array allPoints to be written to files in WritingJointsToFile() /// /// Skeleton object that is sent in to begin tracking the movements of the user /// private void InitializeMoveMaking(Skeleton skeleton, DrawingContext drawingContext) { Joint leftWrist = skeleton.Joints[JointType.WristLeft]; Joint rightWrist = skeleton.Joints[JointType.WristRight]; Joint hipCenter = skeleton.Joints[JointType.HipCenter]; Joint leftKnee = skeleton.Joints[JointType.KneeLeft]; Joint rightKnee = skeleton.Joints[JointType.KneeRight]; Joint leftAnkle = skeleton.Joints[JointType.AnkleLeft]; Joint rightAnkle = skeleton.Joints[JointType.AnkleRight]; Joint shoulderCenter = skeleton.Joints[JointType.ShoulderCenter]; Joint head = skeleton.Joints[JointType.Head]; Joint leftShoulder = skeleton.Joints[JointType.ShoulderLeft]; Joint leftElbow = skeleton.Joints[JointType.ElbowLeft]; Joint rightShoulder = skeleton.Joints[JointType.ShoulderRight]; Joint rightElbow = skeleton.Joints[JointType.ElbowRight]; //checking to see if the correct body parts are in the correct coordinate ranges for the circles leftWIn = LockedInPlace(leftWrist); rightWIn = LockedInPlace(rightWrist); headIn = LockedInPlace(head); leftFIn = LockedInPlace(leftAnkle); rightFIn = LockedInPlace(rightAnkle); //if EVERYTHING is in order, start tracking how many frames (so, how long) they are in that position //If they leave the "locking in" position before a dance starts, they need to start over with trying to lock in //They need to be there for 2 seconds (60 frames) to start if (leftWIn && rightWIn && headIn && leftFIn && rightFIn) //If EVERYTHING is in place, start counting { activatedCount++; if (activatedCount >= 60) //if they have stood in position for at least 2 seconds { activatedCount = 0; if (allIn) //if they are done dancing and standing in the initial position, stop recording data and get ready to write to files { allIn = false; allPoints[0] = hipCenterPoints; allPoints[1] = shoulderCenterPoints; allPoints[2] = headPoints; allPoints[3] = leftShoulderPoints; allPoints[4] = leftElbowPoints; allPoints[5] = leftWristPoints; allPoints[6] = rightShoulderPoints; allPoints[7] = rightElbowPoints; allPoints[8] = rightWristPoints; allPoints[9] = leftKneePoints; allPoints[10] = leftAnklePoints; allPoints[11] = rightKneePoints; allPoints[12] = rightAnklePoints; WritingJointsToFile(allPoints, drawingContext); return; } else //if they in initial position to start the dance, then they are marked as all in and dance moves start being recorded { allIn = true; this.RightHandButton.Fill = Brushes.Lime; this.RightHandButton.Opacity = 1d; this.LeftHandButton.Fill = Brushes.Lime; this.LeftHandButton.Opacity = 1d; this.HeadButton.Fill = Brushes.Lime; this.HeadButton.Opacity = 1d; this.LeftFootButton.Fill = Brushes.Lime; this.LeftFootButton.Opacity = 1d; this.RightFootButton.Fill = Brushes.Lime; this.RightFootButton.Opacity = 1d; } } } else //else if not in initial position, start activatedCount over { activatedCount = 0; } if (allIn) //for when you are no longer in the circles and now recording { frameCount++; if (frameCount == 3) //counting every 3rd frame and recording it { hipCenterPoints.Add(hipCenter.Position); shoulderCenterPoints.Add(shoulderCenter.Position); headPoints.Add(head.Position); leftShoulderPoints.Add(leftShoulder.Position); leftElbowPoints.Add(leftElbow.Position); leftWristPoints.Add(leftWrist.Position); rightShoulderPoints.Add(rightShoulder.Position); rightElbowPoints.Add(rightElbow.Position); rightWristPoints.Add(rightWrist.Position); leftKneePoints.Add(leftKnee.Position); leftAnklePoints.Add(leftAnkle.Position); rightKneePoints.Add(rightKnee.Position); rightAnklePoints.Add(rightAnkle.Position); frameCount = 0; } } } /// /// This function exists to improve the functionality of InitializeMoveMaking() and adding modularity /// to this program. This function checks to see if the correct joints are in the correct place /// (relative to their circles). /// The function takes a Joint object and verifies that it is the correct type before checking it against /// the correct location of the "lock in" circles. /// /// This parameter allows for the function to be applied to all 5 joints that are being checked. /// This function returns true or false depending on whether the joints are lined up with the circles. /// These values are then stored in the corresponding bool variables: leftWIn, rightWIn, headIn, leftFIn, rightFIn /// based on how InitializeMoveMaking() calls this function for each joint /// NOTE: A user has to be between 1.9 and 2.5 meters for this private bool LockedInPlace(Joint joint) { //handling the Left Wrist Brush circleFillTeal = new SolidColorBrush(Color.FromArgb(255, 52, 218, 185)); if (joint.JointType == JointType.WristLeft) { if((joint.Position.X < -0.15 && joint.Position.X > -0.45) && (joint.Position.Y < 0.23 && joint.Position.Y > -0.05) && (joint.Position.Z < 2.5 && joint.Position.Z > 1.9)) { this.LeftHandButton.Fill = Brushes.LightGray; this.LeftHandButton.Opacity = .50d; return true; } else { if (allIn) { this.LeftHandButton.Fill = Brushes.Lime; this.LeftHandButton.Opacity = 1d; } else { this.LeftHandButton.Fill = circleFillTeal; this.LeftHandButton.Opacity = 1d; } return false; } } // handling the Right Wrist else if (joint.JointType == JointType.WristRight) { if ((joint.Position.X > 0.20 && joint.Position.X < 0.36) && (joint.Position.Y < 0.23 && joint.Position.Y > -0.05) && (joint.Position.Z < 2.5 && joint.Position.Z > 1.9)) { this.RightHandButton.Fill = Brushes.LightGray; this.RightHandButton.Opacity = .50d; return true; } else { if (allIn) { this.RightHandButton.Fill = Brushes.Lime; this.RightHandButton.Opacity = 1d; } else { this.RightHandButton.Fill = circleFillTeal; this.RightHandButton.Opacity = 1d; } return false; } } //handling the Head else if (joint.JointType == JointType.Head) { if ((joint.Position.X < 0.06 && joint.Position.X > -0.06) && (joint.Position.Y < 1.0 && joint.Position.Y > 0.60) && (joint.Position.Z < 2.5 && joint.Position.Z > 1.9)) { this.HeadButton.Fill = Brushes.LightGray; this.HeadButton.Opacity = .50d; return true; } else { if (allIn) { this.HeadButton.Fill = Brushes.Lime; this.HeadButton.Opacity = 1d; } else { this.HeadButton.Fill = circleFillTeal; this.HeadButton.Opacity = 1d; } return false; } } //handling the Left Ankle else if(joint.JointType == JointType.AnkleLeft) { if ((joint.Position.X < -0.14 && joint.Position.X > -0.40) && (joint.Position.Y < -.52 && joint.Position.Y > -.73) && (joint.Position.Z < 2.5 && joint.Position.Z > 1.9)) { this.LeftFootButton.Fill = Brushes.LightGray; this.LeftFootButton.Opacity = .50d; return true; } else { if (allIn) { this.LeftFootButton.Fill = Brushes.Lime; this.LeftFootButton.Opacity = 1d; } else { this.LeftFootButton.Fill = circleFillTeal; this.LeftFootButton.Opacity = 1d; } return false; } } //handline the Right Ankle else if (joint.JointType == JointType.AnkleRight) { if ((joint.Position.X < 0.25 && joint.Position.X > 0.07) && (joint.Position.Y < -.52 && joint.Position.Y > -0.73) && (joint.Position.Z < 2.5 && joint.Position.Z > 1.9)) { this.RightFootButton.Fill = Brushes.LightGray; this.RightFootButton.Opacity = .50d; return true; } else { if (allIn) { this.RightFootButton.Fill = Brushes.Lime; this.RightFootButton.Opacity = 1d; } else { this.RightFootButton.Fill = circleFillTeal; this.RightFootButton.Opacity = 1d; } return false; } } return false; } /// /// Maps a SkeletonPoint to lie within our render space and converts to Point /// Created by Microsoft /// /// point to map /// mapped point /// 2/19/2019 as of this date, this function has not been modified, even the documentation private Point SkeletonPointToScreen(SkeletonPoint skelpoint) { // Convert point to depth space. // We are not using depth directly, but we do want the points in our 640x480 output resolution. DepthImagePoint depthPoint = this.sensor.CoordinateMapper.MapSkeletonPointToDepthPoint(skelpoint, DepthImageFormat.Resolution640x480Fps30); return new Point(depthPoint.X, depthPoint.Y); } /// /// Draws a bone line between two joints /// /// skeleton to draw bones from /// drawing context to draw to /// joint to start drawing from /// joint to end drawing at /// 2/19/2019 as of this date, this function has not been modified /// 4/14/2019 as of this date, I added checks for inFreestyle and inMove to change /// the color of the skeleton being drawn in those modes. I maintained the idea of the inferred /// joints to allow the user to understand how their movements may be deemed eratic (since the program /// is guessing where some joints are) private void DrawBone(Skeleton skeleton, DrawingContext drawingContext, JointType jointType0, JointType jointType1) { Joint joint0 = skeleton.Joints[jointType0]; Joint joint1 = skeleton.Joints[jointType1]; // If we can't find either of these joints, exit if (joint0.TrackingState == JointTrackingState.NotTracked || joint1.TrackingState == JointTrackingState.NotTracked) { return; } // Don't draw if both points are inferred if (joint0.TrackingState == JointTrackingState.Inferred && joint1.TrackingState == JointTrackingState.Inferred) { return; } // We assume all drawn bones are inferred unless BOTH joints are tracked Pen drawPen = this.inferredBonePen; if (joint0.TrackingState == JointTrackingState.Tracked && joint1.TrackingState == JointTrackingState.Tracked) { if (inMove) { if (!allIn) { drawPen = this.allInPen; } else { drawPen = this.movePen; } } else if (inFreestyle) { drawPen = this.freestylePen; } } drawingContext.DrawLine(drawPen, this.SkeletonPointToScreen(joint0.Position), this.SkeletonPointToScreen(joint1.Position)); } } }