/// <summary> /// Create the fragmentation points in the FragmentPanel /// </summary> public override void Execute() { foreach (KeyValuePair <Sketch.Stroke, List <int> > entry in ptsToAdd) { Sketch.Stroke stroke = entry.Key; List <int> newIndices = entry.Value; // Associate the split point we've found with our FeatureStroke if (!this.strokeToCorners.ContainsKey(stroke)) { this.strokeToCorners.Add(stroke, newIndices.GetRange(0, newIndices.Count)); DisplayFragmentPoints(stroke, newIndices); } else { List <int> oldIndices = this.strokeToCorners[stroke]; foreach (int index in newIndices) { if (!oldIndices.Contains(index)) { oldIndices.Add(index); DisplayFragmentPoints(stroke, index); } // This Command currently only works with a UNIQUE set of pts in ptsToAdd else { break; } } } } }
/// <summary> /// Undoes the current fragmentation within the FragmentPanel /// </summary> public override void UnExecute() { // Remove all of the fragment point references from the Hashtable foreach (KeyValuePair <Sketch.Stroke, List <int> > entry in this.ptsToAdd) { Sketch.Stroke stroke = entry.Key; List <int> addedIndices = entry.Value; List <int> indices = this.strokeToCorners[stroke]; foreach (int index in addedIndices) { indices.Remove(index); } } // Remove all the drawn strokes from the InkOverlay foreach (System.Drawing.Point coordinates in this.fragmentPtsDrawn) { foreach (Microsoft.Ink.Stroke fragPt in this.overlayInk.Ink.Strokes) { if (fragPt.BezierPoints[0] == coordinates) { this.overlayInk.Ink.DeleteStroke(fragPt); break; } } } }
/// <summary> /// When the user manually triggers recognition, label everything /// in the sketch as a wire. /// </summary> void Recognizer.recognize(Sketch.Sketch sketch, Sketch.Stroke[] selectedStrokes, bool userTriggered) { // Only operate on user triggered recognition events if (!userTriggered) { return; } lastLabeledSketchArgs = new RecognizerEventArgs(); foreach (Sketch.Stroke selectedStroke in selectedStrokes) { if (selectedStroke == null) { // The recognition triggerer left empty elements in // the selected strokes array. Here we just ignore // the lack of a stroke. continue; } // Get the corresponding stroke in the sketch to make sure we label // the right substrokes Sketch.Stroke sketchStroke = sketch.GetStroke((System.Guid)selectedStroke.XmlAttrs.Id); if (sketchStroke == null) { // The selected stroke is not in the sketch. // In some scenarios, we might want to throw an exception, // but in this case we just skip the stroke continue; } // Now label the substrokes Sketch.Substroke[] selectedSubstrokes = sketchStroke.Substrokes; foreach (Sketch.Substroke selectedSubstroke in selectedSubstrokes) { if (selectedSubstroke.GetLabels().Length > 0) { continue; } // Apply wire label or error label using simulated error rate if (randobj.NextDouble() < UIConstants.ErrorLabelErrorRate) { sketch.AddLabel(selectedSubstroke, UIConstants.ErrorLabel, 1.0); } else { sketch.AddLabel(selectedSubstroke, UIConstants.WireLabel, 1.0); } } } lastLabeledSketchArgs.recognitionResults[0] = sketch; lastLabeledSketchArgs.eventTime = DateTime.Now; lastLabeledSketchArgs.userTriggered = true; RecognitionEvent(this, lastLabeledSketchArgs); }
/// <summary> /// Adds an Ink Stroke and updates the Sketch. NOTE: Does NOT check /// to ensure that the given Ink stroke is contained within the /// Ink object associated with this InkSketch. /// </summary> /// <param name="iStroke">The Ink Stroke to add</param> public void AddInkStroke(Microsoft.Ink.Stroke iStroke) { Sketch.Stroke sStroke = mReadJnt.InkStroke2SketchStroke(iStroke); ink2sketchStr.Add(iStroke.Id, sStroke.Substrokes[0].XmlAttrs.Id); // Converted stroke will always have // exactly one substroke. sketchStr2ink.Add(sStroke.Substrokes[0].XmlAttrs.Id, iStroke.Id); substrokeIdMap.Add(sStroke.Substrokes[0].XmlAttrs.Id, sStroke.Substrokes[0]); Sketch.AddStroke(sStroke); if (StrokeAdded != null) { StrokeAdded(sStroke); } }
/// <summary> /// Transforms (e.g. moves or resizes) an Ink Stroke in the Sketch. /// Converts the given Ink Stroke into a Sketch stroke and then /// updates the Sketch to point to the new Ink Stroke instead of the old one. /// </summary> /// <param name="iStroke">The Ink Stroke to modify</param> public void TransformInkStroke(Microsoft.Ink.Stroke iStroke) { if (!ink2sketchStr.ContainsKey(iStroke.Id)) { return; } if (!substrokeIdMap.ContainsKey(ink2sketchStr[iStroke.Id])) { return; } Sketch.Stroke newSStroke = mReadJnt.InkStroke2SketchStroke(iStroke); Substroke oldSStroke = substrokeIdMap[ink2sketchStr[iStroke.Id]]; // FIXME need to masquerade Substroke Ids. Here // we abitrarily synchronize the Ids of the first // substroke in each stroke. We do this because // ReadJnt will always return a stroke with one // substroke. In some use cases (e.g. fragmenting), // matching Ids like this might cause problems. // // Eric Peterson: June 30, 2009: When loading an old // xml sketch, we need to preserve the time data for // each point. newSStroke.Substrokes[0].XmlAttrs.Id = oldSStroke.XmlAttrs.Id; for (int i = 0; i < oldSStroke.Points.Length; i++) { newSStroke.Substrokes[0].Points[i].Time = oldSStroke.Points[i].Time; } // Update mapping between strokes ink2sketchStr.Remove(iStroke.Id); ink2sketchStr.Add(iStroke.Id, newSStroke.Substrokes[0].XmlAttrs.Id); sketchStr2ink.Remove(oldSStroke.XmlAttrs.Id); sketchStr2ink.Add(newSStroke.Substrokes[0].XmlAttrs.Id, iStroke.Id); substrokeIdMap.Remove(newSStroke.Substrokes[0].XmlAttrs.Id); substrokeIdMap.Add(newSStroke.Substrokes[0].XmlAttrs.Id, newSStroke.Substrokes[0]); // Update pointers in Sketch Sketch.Substroke newSSubstroke = newSStroke.Substrokes[0]; foreach (Sketch.Shape shape in oldSStroke.ParentShapes) { shape.AddSubstroke(newSSubstroke); } Sketch.RemoveSubstroke(oldSStroke); Sketch.AddStroke(newSStroke); }
private void DisplayFragmentPoints(Sketch.Stroke stroke, int index) { Sketch.Point[] points = stroke.Points; System.Drawing.Point coordinates = new System.Drawing.Point((int)points[index].X, (int)points[index].Y); // Set the fragment point with drawing attributes Microsoft.Ink.Stroke fragPt = overlayInk.Ink.CreateStroke(new System.Drawing.Point[1] { coordinates }); fragPt.DrawingAttributes = this.fragmentPtAttributes; this.fragmentPtsDrawn.Add(coordinates); }
private void DisplayFragmentPoints(Sketch.Stroke stroke, List <int> indices) { Sketch.Point[] points = stroke.Points; foreach (int index in indices) { System.Drawing.Point pt = new System.Drawing.Point((int)points[index].X, (int)points[index].Y); // Set the fragment point with drawing attributes Microsoft.Ink.Stroke fragPt = this.overlayInk.Ink.CreateStroke(new System.Drawing.Point[1] { pt }); fragPt.DrawingAttributes = this.fragmentPtAttributes; } }
/// <summary> /// Returns the time it took to draw the stroke /// </summary> /// <param name="stroke">The stroke</param> /// <returns>the total time it took to draw the stroke</returns> public ulong findStrokeTime(Sketch.Stroke stroke) { double minTime = Double.PositiveInfinity; double maxTime = Double.NegativeInfinity; Point[] points = stroke.Points; for (int i = 0; i < points.Length; i++) { if ((Double)(points[i].Time) < minTime) { minTime = points[i].Time; } if ((Double)(points[i].Time) > maxTime) { maxTime = (Double)(points[i].Time); } } return((ulong)(maxTime - minTime)); }
/// <summary> /// Adds a Stroke to our internal data structures - the Sketch and FeatureSketch /// </summary> /// Precondition: The InkStroke is not in the InkCanvas's collection of strokes. /// Postcondition: The InkStroke has been added to the InkCanvas, Sketch's list of /// Strokes, Substrokes, and FeatureSketch. /// <param name="inkStroke"></param> public void AddStroke(System.Windows.Ink.Stroke inkStroke) { if (mInkCanvas.Strokes.Contains(inkStroke)) { throw new Exception("adding a stroke to the InkCanvasSketch, but it was already there!"); } // Format the stroke inkStroke.AddPropertyData(dtGuid, (ulong)DateTime.Now.ToFileTime()); string strokeId = Guid.NewGuid().ToString(); inkStroke.AddPropertyData(idGuid, strokeId); // Update the dictionaries Sketch.Stroke sketchStroke = new Sketch.Stroke(inkStroke, dtGuid, SAMPLE_RATE); ink2sketchStr.Add(strokeId, sketchStroke.Substrokes[0].XmlAttrs.Id); sketchStr2ink.Add(sketchStroke.Substrokes[0].Id, strokeId); substrokeIdMap.Add(sketchStroke.Substrokes[0].Id, sketchStroke.Substrokes[0]); // Add it to the relevant data structures (InkCanvas last.) mFeatureSketch.AddStroke(sketchStroke); mInkCanvas.Strokes.Add(inkStroke); }
/// <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); }
public void UpdateFragmentCorners(Dictionary <Sketch.Stroke, List <int> > fragStrokeToCorners) { // Clear the current InkOverlay overlayInk.Ink.DeleteStrokes(); // Split the Stroke at the new fragment points foreach (KeyValuePair <Sketch.Stroke, List <int> > entry in fragStrokeToCorners) { Sketch.Stroke stroke = entry.Key; List <int> corners = entry.Value; bool notEqualOrNull = false; List <int> sCorn; bool haveCorners = this.lTool.StrokeToCorners.TryGetValue(stroke, out sCorn); if (haveCorners) { notEqualOrNull = true; } else if (!haveCorners && corners != null && corners.Count > 0) { notEqualOrNull = true; } else if (haveCorners && corners != null && corners.ToArray() != sCorn.ToArray()) { notEqualOrNull = true; } if (notEqualOrNull) { if ((corners == null || corners.Count == 0) && sCorn.Count > 0) { this.lTool.StrokeToCorners.Remove(stroke); } else { this.lTool.StrokeToCorners[stroke] = corners; } //// Remove all of the substrokes within our InkPicture and hashtables //foreach (Sketch.Substroke substroke in stroke.Substrokes) //{ // this.lTool.MIdToSubstroke.Remove(this.lTool.SubstrokeIdToMStroke[substroke.XmlAttrs.Id.Value].Id); // this.inkPic.Ink.DeleteStroke(this.lTool.SubstrokeIdToMStroke[substroke.XmlAttrs.Id.Value]); // this.lTool.SubstrokeIdToMStroke.Remove(substroke.XmlAttrs.Id.Value); //} // Merge the substrokes together bool labelSame = stroke.MergeSubstrokes(); if (!labelSame) { System.Windows.Forms.DialogResult ok = System.Windows.Forms.MessageBox.Show( "Labels removed from Stroke " + stroke.XmlAttrs.Id.ToString() + "\ndue to fragmenting a non-uniformly labeled stroke", "Important", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Exclamation); } // Fragment the substroke at the specified indices if (corners != null && corners.Count > 0) { stroke.SplitStrokeAt(corners.ToArray()); } // Draw the substrokes in the InkPicture foreach (Sketch.Substroke substroke in stroke.Substrokes) { Sketch.Point[] substrokePts = substroke.Points; System.Drawing.Point[] pts = new System.Drawing.Point[substrokePts.Length]; int len2 = pts.Length; for (int i = 0; i < len2; ++i) { pts[i] = new System.Drawing.Point((int)(substrokePts[i].X), (int)(substrokePts[i].Y)); } this.inkPic.Ink.CreateStroke(pts); // Allows us to look up a Sketch.Stroke from its Microsoft counterpart int mId = this.inkPic.Ink.Strokes[this.inkPic.Ink.Strokes.Count - 1].Id; } // Update the colors associated with the substrokes UpdateColors(new List <Substroke>(stroke.Substrokes)); } } // Add the InkOverlay points we'll be drawing List <System.Drawing.Point> ptsToDraw = new List <System.Drawing.Point>(); Sketch.Stroke[] strokes = this.Sketch.Strokes; foreach (Sketch.Stroke stroke in strokes) { List <int> corners; if (this.lTool.StrokeToCorners.TryGetValue(stroke, out corners)) { Sketch.Point[] points = stroke.Points; foreach (int index in corners) { ptsToDraw.Add(new System.Drawing.Point((int)points[index].X, (int)points[index].Y)); } } } System.Drawing.Point[] fragPts = ptsToDraw.ToArray(); // Create strokes consisting of one point each. // This way we can draw the points in our InkPicture and correctly scale the points // accordingly. foreach (System.Drawing.Point pt in fragPts) { System.Drawing.Point[] p = new System.Drawing.Point[1]; p[0] = pt; overlayInk.Ink.CreateStroke(p); } // Render each point foreach (Microsoft.Ink.Stroke s in overlayInk.Ink.Strokes) { s.DrawingAttributes = this.fragmentPtAttributes; } // Update the Hashtable foreach (KeyValuePair <Sketch.Stroke, List <int> > entry in fragStrokeToCorners) { Sketch.Stroke key = entry.Key; List <int> val = entry.Value; if (val == null || val.Count == 0) { this.lTool.StrokeToCorners.Remove(key); } else { this.lTool.StrokeToCorners[key] = new List <int>(val); } } this.inkPic.Invalidate(); this.inkPic.Refresh(); }
/// <summary> /// Fragment the loaded Stroke at the intersections created by the drawn /// Microsoft.Ink.Stroke. /// </summary> /// <param name="sender">Reference to the object that raised the event</param> /// <param name="e">Passes an object specific to the event that is being handled</param> private void sketchInk_Stroke(object sender, InkCollectorStrokeEventArgs e) { // Find the float intersection points float[] intersections = e.Stroke.FindIntersections(sketchInk.Ink.Strokes); System.Drawing.Point[] pointInter = new System.Drawing.Point[intersections.Length]; // Find the actual points of intersection for (int i = 0; i < pointInter.Length; ++i) { pointInter[i] = LocatePoint(e.Stroke, intersections[i]); } // Remove our drawing stroke from the Ink this.sketchInk.Ink.DeleteStroke(e.Stroke); // Threshold for how far away we can draw from the actual stroke const double DISTTHRESHOLD = 40.0; // Hashtable mapping new points to FeatureStrokes Dictionary <Sketch.Stroke, List <int> > ptsToAdd = new Dictionary <Sketch.Stroke, List <int> >(); // For each point in our intersections we'll find the closest point in // the corresponding FeatureStroke points foreach (System.Drawing.Point currPt in pointInter) { int bestPointIndex = 0; double bestDist = Double.PositiveInfinity; Sketch.Stroke bestStroke = null; // Check all of the FeatureStrokes to see if the Point is close enough foreach (Sketch.Stroke stroke in this.strokes) { Sketch.Point[] pts = stroke.Points; // Find the closest point for this FeatureStroke for (int i = 0; i < pts.Length; i++) { double currDist = Euclidean(currPt.X, currPt.Y, pts[i].X, pts[i].Y); if (currDist < bestDist) { bestDist = currDist; bestPointIndex = i; bestStroke = stroke; } } } // If it's close enough, add it to our temporary Hashtable to keep track of // which point corresponds to which FeatureStroke if (bestDist < DISTTHRESHOLD) { bool alreadyExists = false; if (this.strokeToCorners.ContainsKey(bestStroke)) { List <int> existingPts = this.strokeToCorners[bestStroke]; if (existingPts.Contains(bestPointIndex)) { alreadyExists = true; } } if (!alreadyExists) { if (!ptsToAdd.ContainsKey(bestStroke)) { List <int> newPts = new List <int>(); newPts.Add(bestPointIndex); ptsToAdd.Add(bestStroke, newPts); } else { List <int> existingPts = ptsToAdd[bestStroke]; if (!existingPts.Contains(bestPointIndex)) { existingPts.Add(bestPointIndex); } } } } } if (ptsToAdd.Count > 0) { // Hand-fragment the stroke CM.ExecuteCommand(new CommandList.HandFragmentCornersCmd(ptsToAdd, this.strokeToCorners, this.overlayInk, this.fragmentPtAttributes)); } sketchInk.Refresh(); }
/// <summary> /// Creates a copy of the stroke /// </summary> /// <returns></returns> public Stroke Clone() { Stroke clone = new Stroke(this.CloneSubstrokes(), this.XmlAttrs.Clone()); return(clone); }