{"id":80,"date":"2009-10-15T18:17:17","date_gmt":"2009-10-15T23:17:17","guid":{"rendered":"http:\/\/dotnet.cubicleninja.com\/?p=80"},"modified":"2026-04-23T17:43:55","modified_gmt":"2026-04-23T22:43:55","slug":"etch-a-sketch-emulator-with-the-htc-fuze-grav-sensor","status":"publish","type":"post","link":"https:\/\/blog.cubicleninja.com\/?p=80","title":{"rendered":"Etch-A-Sketch Emulator with the HTC Fuze Grav Sensor (G-Sensor)"},"content":{"rendered":"<p>The premise behind this application was an etch-a-sketch style application I saw on a friend&#8217;s iPhone and a related comment they made about a missing feature&#8230;also, to be honest, my desire to prove that my Windows Mobile based phone could be just as &#8220;snazzy.&#8221;<\/p>\n<p>Rather than trying to re-invent the wheel I set off of a grand adventure to track down an API for the HTC sensors (mainly the G-Sensor, GSensor, Grav Sensor or tilt sensor, depending on what you want to call it).  The hunt began with a <a href=\"https:\/\/google.com\/\" target=\"_blank\">Google<\/a> search for &#8220;htc fuze sensor api.&#8221;  Additionally, my hunt ended with a <a href=\"https:\/\/google.com\/\" target=\"_blank\">Google<\/a> search for &#8220;htc fuze sensor api.&#8221;  On the first page of results I found a link to a <a href=\"https:\/\/blog.enterprisemobile.com\/2008\/07\/using-htc-diamonds-sensor-sdk-from-managed-code\/\" target=\"_blank\">blog post<\/a> on Enterprise Mobile that appeared to be exactly what I needed (their information was greatly assisted by information from <a href=\"https:\/\/scottandmichelle.net\/scott\/comments.html?entry=784\" target=\"_blank\">another blog<\/a> that had reverse engineered the HTC dll that controlled the interaction with the sensors on the Fuze).<\/p>\n<p>I played around for a bit and everything worked well but I had issues with the grav sensor not properly detecting when it was &#8220;at rest&#8221; on a level surface as well as some flakiness in other areas.  I cracked open the source for the wrapper and decided to play around with how it returned the vectors from the sensor.  After several tweaks and adjustments I settled on the following change:<\/p>\n<p>Original code in HTCGSensor.cs:<\/p>\n<pre class=\"lang:js decode:true\">\n        public override GVector GetGVector()\n        {\n            GVector ret = new GVector();\n            HTCGSensorData data = GetRawSensorData();\n            ret.X = data.TiltX;\n            ret.Y = data.TiltY;\n            ret.Z = data.TiltZ;\n            \/\/ HTC's Sensor returns a vector which is around 1000 in length on average..\n            \/\/ but it really depends on how the device is oriented.\n            \/\/ When simply face up, my Diamond returns a vector of around 840 in length.\n            \/\/ While face down, it returns a vector of around 1200 in length.\n            \/\/ The vector direction is fairly accurate, however, the length is clearly not extremely precise.\n            double htcScaleFactor = 1.0 \/ 1000.0 * 9.8;\n            return ret.Scale(htcScaleFactor);\n        }\n<\/pre>\n<p>New version I&#8217;m using:<\/p>\n<pre class=\"lang:js decode:true\">\n        public override GVector GetGVector()\n        {\n            GVector ret = new GVector();\n            HTCGSensorData data = GetRawSensorData();\n            ret.X = Math.Round((data.TiltX\/50.0), 1);\n            ret.Y = Math.Round((data.TiltY\/50.0), 1);\n            ret.Z = Math.Round((data.TiltZ\/50.0), 1);\n\n            return ret;\n        }\n<\/pre>\n<p>I round off the double value to 1 digit post decimal in order to keep the readings smooth.  Using it this way I was able to get fluid-like smoothness in my sensor data as well as accurate readings for &#8220;at rest&#8221; in any position.  I created a couple test apps with a little ball that could bounce off the walls and make the phone vibrate when a wall was hit at a certain velocity and so forth until I was satisfied that the sensor readings were going to provide me the results I was looking to get.<\/p>\n<p>My next step was to create the tilt-to-draw application.  To keep it simple I decided to just stick with good ol&#8217; fashioned bitmap objects.  The key things I wanted to achieve were:<\/p>\n<ol>\n<li>Selectable color from a palette<\/li>\n<li>Adjustable line thickness<\/li>\n<li>Smooth drawing with grav sensor<\/li>\n<li>Ability to save your drawing to the phone<\/li>\n<\/ol>\n<p>And so I began by mocking up a quick layout that I would use<br \/>\n<img decoding=\"async\" src=\"https:\/\/cubicleninja.com\/images\/Tilt_Design.png\" alt=\"Tilt &#038; Draw Design\" \/><\/p>\n<p>I decided to make the tilt drawing aspect of it a toggle so that you could also draw with your finger \/ stylus if you so desired and settled on a slider for line thickness and 4 colors (red, blue, black and green).   Before wiring up any of the controls I went ahead and did a test deploy to get an idea of how it was going to look and was dismayed at how the windows mobile task bar cluttered things up.  First order of business&#8230;hide that task bar while the app is running!<\/p>\n<p>To achieve this goal I needed to import to functions from coredll.dll and then make the proper calls within my initialization (and turn it back when the app was closing):<\/p>\n<pre class=\"lang:js decode:true\">\n        [DllImport(\"coredll.dll\")]\n        private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);\n        [DllImport(\"coredll.dll\")]\n        private static extern IntPtr ShowWindow(IntPtr hWnd, int visible);\n\n        public SampleDrawing()\n        {\n            InitializeComponent();\n\n            ShowWindow(FindWindow(\"HHTaskBar\", null), 0); \/\/0=hide, 1=show\n        }\n<\/pre>\n<p>With that out of the way I began wiring everything up.   I&#8217;ve attached the solution at the bottom of the post so I&#8217;m going to bypass listing out the generic code and just show the pertinent bits.<\/p>\n<p>You&#8217;ll notice the reference to curPos in that function.  I maintained two vector objects:  curPos and lastPos to enable the smooth drawing of the lines around the screen.   The variables were updated either by the &#8220;mouse&#8221; being moved around the screen or the grav sensor telling it the position needed to update (for this function I also used a check to keep the paintbrush within the bounds of the screen).<\/p>\n<pre class=\"lang:js decode:true\">\n        private void SampleDrawing_MouseDown(object sender, MouseEventArgs e)\n        {\n            lastPos.X = curPos.X = ((MouseEventArgs)e).X;\n            lastPos.Y = curPos.Y = ((MouseEventArgs)e).Y;\n        }\n\n        private void SampleDrawing_MouseMove(object sender, MouseEventArgs e)\n        {\n            if (!tiltEnable.Checked && allowDrawing)\n            {\n                curPos.X = e.X;\n                curPos.Y = e.Y;\n                drawGraphics();\n            }\n        }\n\n        void CalculatePhysics(GVector gVector, int elapsedTime)\n        {\n            int maxX = ClientSize.Width - (int)myThickness;\n            int maxY = ClientSize.Height - 45 - (int)myThickness;\n\n            gVector = gVector.Scale(1 \/ friction);\n\n            if (!(curPos.X <= 0 &#038;&#038; gVector.X < 0) &#038;&#038; !(curPos.X >= maxX && gVector.X > 0))\n            {\n                velocity.X = gVector.X * elapsedTime \/ 1000;\n                curPos.X += velocity.X * elapsedTime \/ 1000;\n            }\n            if (!(curPos.Y <= 0 &#038;&#038; gVector.Y < 0) &#038;&#038; !(curPos.Y >= maxY && gVector.Y > 0))\n            {\n                velocity.Y = gVector.Y * elapsedTime \/ 1000;\n                curPos.Y += velocity.Y * elapsedTime \/ 1000;\n            }\n        }\n<\/pre>\n<p>The actual drawing of the line happens the same regardless of how the cursor was moved and, to be honest, I had a bit of trouble determining an efficient method for drawing the line.   I couldn&#8217;t just use the standard line methods because I wanted to be able to have variable thickness and that doesn&#8217;t mesh well with the normal line draw methods.  To get around the issue of jaggy and gap-filled lines I opted to use the FillEllipse method.   This gave the undesired effect of giving me variably sized dotted lines.  My final solution uses a bit of generic math and a loop to draw the ellipses in a way that will create a &#8220;filled in&#8221; line.  I&#8217;m certain there&#8217;s a better way to do this but darned if I was able to come up with it (Suggestions are VERY welcome).<\/p>\n<pre class=\"lang:js decode:true\">\n        void drawGraphics()\n        {\n            \/\/ get tick count so we can calculate the time elapsed since last paint\n            int thisPaint = Environment.TickCount;\n            \/\/ put the vector into screen space\n            if (tiltEnable.Checked)\n            {\n                GVector gvector = mySensor.GetGVector();\n                gvector = gvector.Scale(htcDPI*friction);\n                CalculatePhysics(gvector, thisPaint - lastPaint);\n            }\n\n            CalculateBounds();\n\n            double rise = (lastPos.Y - curPos.Y);\n            double run = (lastPos.X - curPos.X);\n            double mult = (myThickness\/2);\n\n            if (Math.Abs(rise) > Math.Abs(run) && Math.Abs(rise) > mult)\n                mult \/= Math.Abs(rise);\n            else if (Math.Abs(run) > mult)\n                mult \/= Math.Abs(run);\n\n            rise *= mult;\n            run *= mult;\n\n            if (Math.Abs(run) > 0 || Math.Abs(rise) > 0)\n            {\n                while (((rise < 0 &#038;&#038; lastPos.Y < curPos.Y) ||\n                        (rise > 0 && lastPos.Y > curPos.Y)) ||\n                       ((run < 0 &#038;&#038; lastPos.X < curPos.X) ||\n                        (run > 0 && lastPos.X > curPos.X))\n                       )\n                {\n                    backBuffer.FillEllipse(myBrush, (int)lastPos.X, (int)lastPos.Y, (int)myThickness, (int)myThickness);\n                    lastPos.X -= run;\n                    lastPos.Y -= rise;\n                }\n            }\n            lastPos = curPos;\n\n            g.DrawImage(backBufferImage, 0, 0);\n\n            lastPaint = thisPaint;\n        }\n<\/pre>\n<p>This worked well enough to give me a smoothly flowing line on my Fuze and provided a decent-enough response time on things that it felt like you really were drawing as you tilted the screen around.  At this point I had 3 of my 4 requirements completed and just needed the ability to save out the image.  Luckily this ended up being trivial due to my choice of the bitmap object for drawing.   I simply wired up the save button with the following code and was set.<\/p>\n<pre class=\"lang:js decode:true\">\n        private void btnSave_Click(object sender, EventArgs e)\n        {\n            SaveFileDialog tmpDialog = new SaveFileDialog();\n            tmpDialog.Filter = \"Bitmap (*.bmp)|*.bmp|JPEG (*.jpg)|*.jpg|GIF (*.gif)|*.gif|PING (*.png)|*.png\";\n\n            if (tmpDialog.ShowDialog() == DialogResult.OK)\n            {\n                string tmpFileName = tmpDialog.FileName;\n                ImageFormat tmpFormat = ImageFormat.Bmp;\n\n                switch (tmpFileName.Substring(tmpFileName.Length - 4).ToLower())\n                {\n                    case \".bmp\":\n                        tmpFormat = ImageFormat.Bmp;\n                        break;\n                    case \".jpg\":\n                    case \"jpeg\":\n                        tmpFormat = ImageFormat.Jpeg;\n                        break;\n                    case \".gif\":\n                        tmpFormat = ImageFormat.Gif;\n                        break;\n                    case \".png\":\n                        tmpFormat = ImageFormat.Png;\n                        break;\n\n                }\n                backBufferImage.Save(tmpFileName, tmpFormat);\n            }\n            if (backBufferImage != null)\n            {\n                backBufferImage.Dispose();\n                backBufferImage = null;\n            }\n            if (backBuffer != null)\n            {\n                backBuffer.Dispose();\n                backBuffer = null;\n            }\n            backBufferImage = new Bitmap(ClientSize.Width, ClientSize.Height, PixelFormat.Format16bppRgb565);\n            backBuffer = Graphics.FromImage(backBufferImage);\n            backBuffer.Clear(Color.White);\n            g.DrawImage(backBufferImage, 0, 0);\n        }\n<\/pre>\n<p>I was fairly happy with the application but then the dreaded snarky comment was made:  &#8220;Yeah, that&#8217;s neat, but you can&#8217;t turn it upside down and shake it to clear the screen like you can with a real etch-a-sketch.&#8221;  I hate snark with a deep fiery passion&#8230;ok, that&#8217;s a lie I absolutely love it, and I also enjoy new ideas.  I added in the feature of turning it upside down and &#8220;shaking it&#8221; to clear the screen.  I put &#8220;shaking it&#8221; in quotes because, well, you don&#8217;t really have to shake it, you just have to turn it upside down (please don&#8217;t tell).<\/p>\n<pre class=\"lang:js decode:true\">\n        void mySensor_OrientationChanged(IGSensor sender)\n        {\n            if (sender.Orientation == ScreenOrientation.FaceDown)\n            {\n                btnClear_Click(null, null);\n                allowDrawing = false;\n            }\n            else if (!allowDrawing)\n            {\n                allowDrawing = true;\n                curPos = lastPos = new GVector(ClientSize.Width \/ 2, ClientSize.Height \/ 2, 0);\n                lastPaint = Environment.TickCount;\n            }\n        }\n<\/pre>\n<p>And there you have it.   A cute windows mobile app that will let you draw around on the screen to your heart&#8217;s content and save out your beautiful creations to send as an MMS to friends and family the world over.  As promised I&#8217;ve attached the entire solution for the project here.  It includes my compiled version of the Sensors dll.  If you&#8217;d like to get a copy of the DLL source code yourself you can grab it from the links at the top of the post and tweak it til you go blind.<\/p>\n<p><a href=\"https:\/\/cubicleninja.com\/files\/TiltDraw.zip\" target=\"_blank\">Full Source Files<\/a><\/p>\n<p><a href=\"https:\/\/cubicleninja.com\/files\/TiltDraw_Exe.zip\" target=\"_blank\">Exe and Dll for your phone<\/a><\/p>\n<p><a href=\"https:\/\/cubicleninja.com\/files\/Tilt Draw.CAB\">Mobile Installer (CAB)<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>The premise behind this application was an etch-a-sketch style application I saw on a friend&#8217;s iPhone and a related comment they made about a missing feature&#8230;also, to be honest, my desire to prove that my Windows Mobile based phone could be just as &#8220;snazzy.&#8221; Rather than trying to re-invent the wheel I set off of &hellip; <a href=\"https:\/\/blog.cubicleninja.com\/?p=80\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;Etch-A-Sketch Emulator with the HTC Fuze Grav Sensor (G-Sensor)&#8221;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[2,4],"tags":[13,16,23,24,26,27,28,38],"class_list":["post-80","post","type-post","status-publish","format-standard","hentry","category-net","category-csharp","tag-net","tag-c","tag-fuze","tag-g-sensor","tag-grav-sensor","tag-gsensor","tag-htc","tag-tilt-sensor"],"_links":{"self":[{"href":"https:\/\/blog.cubicleninja.com\/index.php?rest_route=\/wp\/v2\/posts\/80","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.cubicleninja.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.cubicleninja.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.cubicleninja.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.cubicleninja.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=80"}],"version-history":[{"count":1,"href":"https:\/\/blog.cubicleninja.com\/index.php?rest_route=\/wp\/v2\/posts\/80\/revisions"}],"predecessor-version":[{"id":21807,"href":"https:\/\/blog.cubicleninja.com\/index.php?rest_route=\/wp\/v2\/posts\/80\/revisions\/21807"}],"wp:attachment":[{"href":"https:\/\/blog.cubicleninja.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=80"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.cubicleninja.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=80"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.cubicleninja.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=80"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}