/// <summary> /// <see cref="InvalidateStroke"/> /// </summary> /// <param name="iStroke">The Ink stroke to invalidate</param> /// <param name="padding">Number of pixels to pad invalidation /// rectangle in Ink space pixels</param> public void InvalidateStroke(Microsoft.Ink.Stroke iStroke, int padding) { using (Graphics g = InkPicture.CreateGraphics()) { Rectangle bb = iStroke.GetBoundingBox(); System.Drawing.Point upperLeft = new System.Drawing.Point(bb.X - padding, bb.Y - padding); System.Drawing.Point lowerRight = new System.Drawing.Point(bb.Right + padding, bb.Bottom + padding); InkPicture.Renderer.InkSpaceToPixel(g, ref upperLeft); InkPicture.Renderer.InkSpaceToPixel(g, ref lowerRight); InkPicture.Invalidate(new Rectangle(upperLeft.X, upperLeft.Y, lowerRight.X, lowerRight.Y)); } }
/// <summary> /// Returns the bounding box for the given shape in ink space coordinates. Optionall colors the strokes /// the default color for non-wire symbols. /// <see cref="UIConstants.TextFeedbackMechanismSymbolColor"/> /// </summary> /// <param name="shape">the shape for which the bounding box will be calculated</param> /// <param name="substroke2InkTable">A hashtable mapping Ink Stroke IDs to Sketch Substroke IDs. /// This hashtable allows the Feedback Mechanism to assocate Ink Strokes drawn to screen /// with their corresponding (labeled) Sketch substrokes.</param> /// <param name="colorStrokes">True iff the microsoft ink strokes should be colored while calculating /// the bounding box (for efficiency's sake).</param> /// <returns>A System.Drawing.Rectangle representing the bounding box /// for the given shape (in ink space coordinates)</returns> private Rectangle calculateBoundingBox(Sketch.Shape shape, Hashtable substroke2InkTable, bool colorStrokes) { // Compute the bounding box of all the ink strokes // associated with this shape Rectangle boundingBox = Rectangle.Empty; foreach (Sketch.Substroke substroke in shape.Substrokes) { if (!substroke2InkTable.Contains(substroke.XmlAttrs.Id)) { // If, for some reason, there is no ink stroke // that corresponds to this substroke, skip it continue; } Microsoft.Ink.Stroke iStroke = (Microsoft.Ink.Stroke)substroke2InkTable[substroke.XmlAttrs.Id]; if (colorStrokes) { iStroke.DrawingAttributes.Color = UIConstants.TextFeedbackMechanismSymbolColor; } Rectangle strokeBox = iStroke.GetBoundingBox(); if (boundingBox.IsEmpty) { boundingBox = strokeBox; } else { if (strokeBox.X < boundingBox.X) { boundingBox.X = strokeBox.X; } if (strokeBox.Y < boundingBox.Y) { boundingBox.Y = strokeBox.Y; } if (strokeBox.Bottom > boundingBox.Bottom) { boundingBox.Height = strokeBox.Bottom - boundingBox.Y; } if (strokeBox.Right > boundingBox.Right) { boundingBox.Width = strokeBox.Right - boundingBox.X; } } } return(boundingBox); }
/// <summary> /// Takes an Ink.Stroke and outputs an internal representation that can /// then be output to an XML file /// </summary> /// <param name="inkStroke">Ink.Stroke which will have extracted from it the information necessary to store a Stroke in MIT XML.</param> /// <param name="transf">Transformation matrix to shift the stroke by</param> /// <param name="shift">Boolean for if we should shift by the transformation matrix</param> /// <returns>PointsAndShape object that stores the extracted information.</returns> public static Sketch.Stroke StripStrokeData(Microsoft.Ink.Stroke inkStroke, Matrix transf, bool shift) { int i; int length; // Get the timestamp for the function using an undocumented GUID (Microsoft.Ink.StrokeProperty.TimeID) to // get the time as a byte array, which we then convert to a long ulong theTime; if (inkStroke.ExtendedProperties.Contains(Microsoft.Ink.StrokeProperty.TimeID) && (inkStroke.ExtendedProperties[Microsoft.Ink.StrokeProperty.TimeID] != null)) { byte[] timeBits = inkStroke.ExtendedProperties[Microsoft.Ink.StrokeProperty.TimeID].Data as byte[]; // Filetime format ulong fileTime = BitConverter.ToUInt64(timeBits, 0); // MIT time format theTime = (fileTime - 116444736000000000) / 10000; //string time = theTime.ToString(); } else { //theTime = (ulong)System.DateTime.Now.Ticks; //theTime = ((ulong)System.DateTime.Now.Ticks - 116444736000000000) / 10000; theTime = ((ulong)DateTime.Now.ToFileTime() - 116444736000000000) / 10000; } // Grab X and Y int[] xData = inkStroke.GetPacketValuesByProperty(Microsoft.Ink.PacketProperty.X); int[] yData = inkStroke.GetPacketValuesByProperty(Microsoft.Ink.PacketProperty.Y); if (shift) { // Shift X and Y according to transformation matrix System.Drawing.Point[] pointData = new System.Drawing.Point[xData.Length]; length = xData.Length; for (i = 0; i < length; i++) { pointData[i] = new System.Drawing.Point(xData[i], yData[i]); } transf.TransformPoints(pointData); //Console.WriteLine("x: " + (pointData[0].X - xData[0]) + " y: " + (pointData[0].Y - yData[0])); for (i = 0; i < length; i++) { xData[i] = pointData[i].X; yData[i] = pointData[i].Y; } } Microsoft.Ink.DrawingAttributes drawingAttributes = inkStroke.DrawingAttributes; System.Drawing.Rectangle boundingBox = inkStroke.GetBoundingBox(); // Grab the color int color = drawingAttributes.Color.ToArgb(); // THIS IS DRAWING POINT (PENTIP) HEIGHT AND WIDTH... WE DONT HAVE ANYWHERE TO PUT IT... //string height = inkStroke.DrawingAttributes.Height.ToString(); //string width = inkStroke.DrawingAttributes.Width.ToString(); float penWidth = drawingAttributes.Width; float penHeight = drawingAttributes.Height; // Grab height and width of total stroke //float height = boundingBox.Height; //float width = boundingBox.Width; // Grab penTip string penTip = drawingAttributes.PenTip.ToString(); // Grab raster string raster = drawingAttributes.RasterOperation.ToString(); //float x = Convert.ToSingle(boundingBox.X); //float y = Convert.ToSingle(boundingBox.Y); //float leftx = Convert.ToSingle(boundingBox.Left); //float topy = Convert.ToSingle(boundingBox.Top); // If the pressure data is included take it, otherwise set to 255/2's // Not all pressure data is in the range of 0 - 255. Some tablets // register pressure in the range 0 - 1023, while others are 0 - 32768 int[] pressureData; if (ReadJnt.IsPropertyIncluded(inkStroke, Microsoft.Ink.PacketProperty.NormalPressure)) { pressureData = inkStroke.GetPacketValuesByProperty(Microsoft.Ink.PacketProperty.NormalPressure); int max = 0; foreach (int p in pressureData) { max = Math.Max(max, p); } if (max > 1024) // Tablet reading in range 0 - 32767 { double conversion = 255.0 / 32767.0; List <int> temp = new List <int>(pressureData.Length); foreach (int p in pressureData) { temp.Add((int)(p * conversion)); } pressureData = temp.ToArray(); } else if (max > 255) // Tablet reading in range 0 - 1023 { double conversion = 255.0 / 1023.0; List <int> temp = new List <int>(pressureData.Length); foreach (int p in pressureData) { temp.Add((int)(p * conversion)); } pressureData = temp.ToArray(); } } else { pressureData = new int[xData.Length]; length = pressureData.Length; for (i = 0; i < length; ++i) { pressureData[i] = (255 - 0) / 2; } } // If the time data is included take it, otherwise set to the timestamp plus and increment ulong[] adjustedTime = new ulong[xData.Length]; int[] timerTick; if (ReadJnt.IsPropertyIncluded(inkStroke, Microsoft.Ink.PacketProperty.TimerTick)) { timerTick = inkStroke.GetPacketValuesByProperty(Microsoft.Ink.PacketProperty.TimerTick); length = timerTick.Length; for (i = 0; i < length; i++) { // This may be incorrect, we never encountered this because Microsoft Journal doesn't save TimerTick data. // We know that the timestamp of a stroke is made when the pen is lifted. // Add the time of the stroke to each offset adjustedTime[i] = theTime + (ulong)timerTick[i] * 10000; } } else { timerTick = new int[xData.Length]; length = timerTick.Length; for (i = 0; i < length; i++) { // We believe this to be the standard sample rate. The multiplication by 1,000 is to convert from // seconds to milliseconds. // // Our time is in the form of milliseconds since Jan 1, 1970 // // NOTE: The timestamp for the stroke is made WHEN THE PEN IS LIFTED adjustedTime[i] = theTime - (ulong)((1 / SAMPLE_RATE * 1000) * (length - i)); } } // Create the array of ID's as new Guid's Guid[] idData = new Guid[xData.Length]; length = idData.Length; for (i = 0; i < length; i++) { idData[i] = Guid.NewGuid(); } // Create the internal representation Sketch.Stroke converterStroke = new Sketch.Stroke(); converterStroke.Name = "stroke"; converterStroke.Time = (ulong)theTime; converterStroke.Source = "Converter"; // Add all of the points to the stroke length = inkStroke.PacketCount; List <Point> pointsToAdd = new List <Point>(); for (i = 0; i < length; i++) { float currX = Convert.ToSingle(xData[i]); float currY = Convert.ToSingle(yData[i]); XmlStructs.XmlPointAttrs attrs = new XmlStructs.XmlPointAttrs( currX, currY, Convert.ToUInt16(pressureData[i]), Convert.ToUInt64(adjustedTime[i]), "point", idData[i]); Sketch.Point toAdd = new Sketch.Point(attrs); } //NOTE: X, Y, WIDTH, HEIGHT done later... boundingbox seems to be off Sketch.Substroke substroke = new Sketch.Substroke(pointsToAdd); substroke.Name = "substroke"; substroke.Color = color; substroke.PenTip = penTip; substroke.PenWidth = penWidth; substroke.PenHeight = penHeight; substroke.Raster = raster; substroke.Source = "ConverterJnt"; substroke.Start = idData[0]; substroke.End = idData[idData.Length - 1]; converterStroke.AddSubstroke(substroke); return(converterStroke); }
/// <summary> /// Takes an Ink.Stroke and outputs an internal representation that can /// then be output to an XML file /// </summary> /// <param name="inkStroke">Ink.Stroke which will have extracted from it the information necessary to store a Stroke in MIT XML.</param> /// <param name="transf">Transformation matrix to shift the stroke by</param> /// <param name="shift">Boolean for if we should shift by the transformation matrix</param> /// <returns>PointsAndShape object that stores the extracted information.</returns> private Sketch.Stroke StripStrokeData(Microsoft.Ink.Stroke inkStroke, Matrix transf, bool shift) { int i; int length; // Get the timestamp for the function using an undocumented GUID (Microsoft.Ink.StrokeProperty.TimeID) to // get the time as a byte array, which we then convert to a long long theTime; if (this.IsPropertyIncluded(inkStroke, Microsoft.Ink.StrokeProperty.TimeID)) { byte[] timeBits = inkStroke.ExtendedProperties[Microsoft.Ink.StrokeProperty.TimeID].Data as byte[]; long fileTime = BitConverter.ToInt64(timeBits, 0); //filetime format theTime = (fileTime - 116444736000000000) / 10000; //MIT time format // string time = theTime.ToString(); } else { theTime = DateTime.Now.Ticks; } // Grab X and Y int[] xData = inkStroke.GetPacketValuesByProperty(Microsoft.Ink.PacketProperty.X); int[] yData = inkStroke.GetPacketValuesByProperty(Microsoft.Ink.PacketProperty.Y); if (shift) { // Shift X and Y according to transformation matrix System.Drawing.Point[] pointData = new System.Drawing.Point [xData.Length]; length = xData.Length; for (i = 0; i < length; i++) { pointData[i] = new System.Drawing.Point(xData[i], yData[i]); } transf.TransformPoints(pointData); //Console.WriteLine("x: " + (pointData[0].X - xData[0]) + " y: " + (pointData[0].Y - yData[0])); for (i = 0; i < length; i++) { xData[i] = pointData[i].X; yData[i] = pointData[i].Y; } } Microsoft.Ink.DrawingAttributes drawingAttributes = inkStroke.DrawingAttributes; System.Drawing.Rectangle boundingBox = inkStroke.GetBoundingBox(); // Grab the color int color = drawingAttributes.Color.ToArgb(); // THIS IS DRAWING POINT (PENTIP) HEIGHT AND WIDTH... WE DONT HAVE ANYWHERE TO PUT IT... //string height = inkStroke.DrawingAttributes.Height.ToString(); //string width = inkStroke.DrawingAttributes.Width.ToString(); // Grab height and width of total stroke float height = boundingBox.Height; float width = boundingBox.Width; // Grab penTip string penTip = drawingAttributes.PenTip.ToString(); // Grab raster string raster = drawingAttributes.RasterOperation.ToString(); float x = Convert.ToSingle(boundingBox.X); float y = Convert.ToSingle(boundingBox.Y); float leftx = Convert.ToSingle(boundingBox.Left); float topy = Convert.ToSingle(boundingBox.Top); // If the pressure data is included take it, otherwise set to 255/2's int[] pressureData; if (this.IsPropertyIncluded(inkStroke, Microsoft.Ink.PacketProperty.NormalPressure)) { pressureData = inkStroke.GetPacketValuesByProperty(Microsoft.Ink.PacketProperty.NormalPressure); } else { pressureData = new int[xData.Length]; length = pressureData.Length; for (i = 0; i < length; ++i) { pressureData[i] = (255 - 0) / 2; } } // If the time data is included take it, otherwise set to the timestamp plus and increment long[] adjustedTime = new long[xData.Length]; int[] timerTick; if (this.IsPropertyIncluded(inkStroke, Microsoft.Ink.PacketProperty.TimerTick)) { timerTick = inkStroke.GetPacketValuesByProperty(Microsoft.Ink.PacketProperty.TimerTick); length = timerTick.Length; for (i = 0; i < length; i++) { // This may be incorrect, we never encountered this because Microsoft Journal doesn't save TimerTick data. // We know that the timestamp of a stroke is made when the pen is lifted. adjustedTime[i] = theTime + (long)timerTick[i] * 10000; //add the time of the stroke to each offset } } else { timerTick = new int[xData.Length]; length = timerTick.Length; for (i = 0; i < length; i++) { // We believe this to be the standard sample rate. The multiplication by 1,000 is to convert from // seconds to milliseconds. // // Our time is in the form of milliseconds since Jan 1, 1970 // // NOTE: The timestamp for the stroke is made WHEN THE PEN IS LIFTED adjustedTime[i] = theTime - (long)((1 / SAMPLE_RATE * 1000) * (length - i)); } } // Create the array of ID's as new Guid's Guid[] idData = new Guid[xData.Length]; length = idData.Length; for (i = 0; i < length; i++) { idData[i] = Guid.NewGuid(); } // Create the internal representation Sketch.Stroke converterStroke = new Sketch.Stroke(); converterStroke.XmlAttrs.Id = System.Guid.NewGuid(); converterStroke.XmlAttrs.Name = "stroke"; converterStroke.XmlAttrs.Time = (ulong)theTime; converterStroke.XmlAttrs.Type = "stroke"; converterStroke.XmlAttrs.Source = "Converter"; Sketch.Substroke substroke = new Sketch.Substroke(); substroke.XmlAttrs.Id = System.Guid.NewGuid(); substroke.XmlAttrs.Name = "substroke"; substroke.XmlAttrs.Time = (ulong)theTime; substroke.XmlAttrs.Type = "substroke"; substroke.XmlAttrs.Color = color; substroke.XmlAttrs.Height = height; substroke.XmlAttrs.Width = width; substroke.XmlAttrs.PenTip = penTip; substroke.XmlAttrs.Raster = raster; substroke.XmlAttrs.X = x; substroke.XmlAttrs.Y = y; substroke.XmlAttrs.LeftX = leftx; substroke.XmlAttrs.TopY = topy; substroke.XmlAttrs.Source = "Converter"; substroke.XmlAttrs.Start = idData[0]; substroke.XmlAttrs.End = idData[idData.Length - 1]; // Add all of the points to the stroke length = inkStroke.PacketCount; for (i = 0; i < length; i++) { Sketch.Point toAdd = new Sketch.Point(); toAdd.XmlAttrs.Name = "point"; toAdd.XmlAttrs.X = Convert.ToSingle(xData[i]); toAdd.XmlAttrs.Y = Convert.ToSingle(yData[i]); toAdd.XmlAttrs.Pressure = Convert.ToUInt16(pressureData[i]); toAdd.XmlAttrs.Time = Convert.ToUInt64(adjustedTime[i]); toAdd.XmlAttrs.Id = idData[i]; substroke.AddPoint(toAdd); } converterStroke.AddSubstroke(substroke); return(converterStroke); }