/* Determines whether the stroke coule be interpreted as a * scratchout by checking that the length is greater than * three times the width of its bounding box, that it has * at least three self intersections, and its height is at * most three quarters of its width. */ public static bool isScratchOut(Stroke s) { Rectangle rect = s.GetBoundingBox(); double length = StrokeManager.StrokeLength(s); return(length > 3 * rect.Width && rect.Height <= rect.Width * 3 / 4 && s.SelfIntersections.Length > 2); }
/* Returns true if the stroke is somewhat closed in the * sense that it comes back to itself. Accomplishes * this by checking the distance between the first point * in the stroke and the last point in the stroke. */ public static bool isClosed(Stroke s, double tolerance) { if (s == null) { return(false); } Point[] pts = { s.GetPoint(0), s.GetPoint(s.PacketCount - 1) }; return(StrokeManager.Distance(pts[0], pts[1]) <= tolerance); }
//Gets the length of a stroke public static double StrokeLength(Stroke e) { double dist = 0; Point[] points = e.GetPoints(); for (int i = 0; i < points.Length - 1; i++) { dist += StrokeManager.Distance(points[i], points[i + 1]); } return(dist); }
public void Render(InkOverlay i, Graphics g) { int width = weight.ToString().Length *15; int height = 18; stroke.DrawingAttributes = i.DefaultDrawingAttributes.Clone(); stroke.DrawingAttributes.Color = color; i.Renderer.Draw(g, stroke); Rectangle rect = StrokeManager.InkSpaceToPixelRect(i, g, stroke.GetBoundingBox()); Point center = new Point(rect.X + rect.Width / 2 - width / 2, rect.Y + rect.Height / 2 - height / 2); g.FillRectangle(new SolidBrush(Color.White), center.X, center.Y, width, height); g.DrawString(weight.ToString(), new Font("Courier New", 14, FontStyle.Bold), new SolidBrush(Color.Black), center); }
public Node(Stroke stroke) { this.stroke = stroke; this.id = stroke.Id; Rectangle r = stroke.GetBoundingBox(); //Determine whether this node is a rectangle or not isRect = r.Width * Math.PI < StrokeManager.StrokeLength(stroke); centerPoint = new Point(r.X + r.Width / 2, r.Y + r.Height / 2); fillColor = DEFAULT; edges = new Edges(); textColor = TEXT_DEFAULT; text = ""; //Set initial distance to infinity (max int) distance = Int32.MaxValue; }
/* Returns true if the stroke resembles a rectangle given a tolerance. * Accomplishes this by taking the perimeter of a rectangle that * would be bounded in the area the stroke is bounded and * comparing it to the length of the stroke. */ public static bool FitsRectProperties(Stroke e, double tolerance) { if (e == null || FitsCircleProperties(e, CIRCLE_TOLERANCE)) { return(false); } Rectangle r = e.GetBoundingBox(); if (r.Height < MIN_WIDTH || r.Width < MIN_WIDTH) { return(false); } double perimeter = r.Height * 2 + r.Width * 2; double strokeLength = StrokeManager.StrokeLength(e); return(Math.Abs(perimeter - strokeLength) <= tolerance); }
/* Returns true if the stroke resembles a circle given a tolerance. * Accomplishes this by taking the perimeter of a circle that * would be bounded in the area the stroke is bounded and * comparing it to the length of the stroke. */ public static bool FitsCircleProperties(Stroke e, double tolerance) { if (e == null) { return(false); } Rectangle r = e.GetBoundingBox(); double radius = StrokeManager.Avg(r.Height, r.Width) / 2.0; if (radius < MIN_WIDTH / 2) { return(false); } double perimeter = 2.0 * radius * Math.PI; Point[] points = e.GetPoints(); double strokeLength = StrokeManager.StrokeLength(e); return(Math.Abs(perimeter - strokeLength) <= tolerance); }
private void inkOverlay_StrokesDeleting(object sender, InkOverlayStrokesDeletingEventArgs e) { if (this.InvokeRequired) { return; } Strokes strokes = e.StrokesToDelete; //Remove the corresponding nodes and edges for each stroke for (int i = 0; i < strokes.Count; i++) { if (StrokeManager.isClosed(strokes[i], 0)) { graph.Remove(graph.Nodes.getNode(strokes[i])); } else { graph.Remove(graph.Edges.getEdge(strokes[i])); } } Invalidate(); }
/* Checks if the stroke drawn is within the range of a * edge from Graph g. The range of an edge is roughly * around its center. */ public static Edge HitEdgeTest(Stroke s, Graph g) { if (StrokeManager.StrokeLength(s) > HIT_TEST_THRESHOLD) { return(null); } float distance = 1300; Edge hitedge = null; for (int i = 0; i < g.Edges.Length(); i++) { float tmp; Edge e = g.Edges[i]; Rectangle rect = e.Stroke.GetBoundingBox(); Point p = new Point(rect.X + rect.Width / 2, rect.Y + rect.Height / 2); s.NearestPoint(p, out tmp); if (tmp < distance) { distance = tmp; hitedge = e; } } return(hitedge); }
public void Render(InkOverlay i, Graphics g) { if (stroke.Deleted == true) { return; } Rectangle rect = StrokeManager.InkSpaceToPixelRect(i, g, stroke.GetBoundingBox()); if (isRect) { g.FillRectangle(new SolidBrush(fillColor), rect); } else { g.FillEllipse(new SolidBrush(fillColor), rect); } i.Renderer.Draw(g, stroke); Point[] p = { new Point(centerPoint.X, centerPoint.Y) }; i.Renderer.InkSpaceToPixel(g, ref p); p[0].X -= 15; p[0].Y -= 10; g.DrawString(text, new Font("Arial", 10), new SolidBrush(textColor), p[0]); }
//Renders each edge and node and also marks the home and destination nodes public void Render(InkOverlay i, Graphics g) { for (int j = 0; j < edges.Length(); j++) { edges[j].Render(i, g); } for (int j = 0; j < nodes.Length(); j++) { nodes[j].Render(i, g); if (nodes[j].Equals(home)) { g.DrawString("*", new Font("Arial", 30), new SolidBrush(Color.Green), StrokeManager.InkSpaceToPixelRect(i, g, nodes[j].Stroke.GetBoundingBox())); } else if (nodes[j].Equals(destination)) { g.DrawString("X", new Font("Arial", 14, FontStyle.Bold), new SolidBrush(Color.Red), StrokeManager.InkSpaceToPixelRect(i, g, nodes[j].Stroke.GetBoundingBox())); } } }
private void inkOverlay_Stroke(object sender, InkCollectorStrokeEventArgs e) { if (this.InvokeRequired) { return; } //in effect, this is reseting the timer to start from the beginning. edgeTimer.Stop(); edgeTimer.Start(); //Check if the stroke was a tap, and if it was, get the node it tapped. Node n = StrokeManager.TappedNode(e.Stroke, graph); if (n != null) { //If its eraser mode, delete it. if (inkOverlay.EditingMode == InkOverlayEditingMode.Delete) { graph.Remove(n); } else { //Any other mode, select it and change to selection mode int[] ids = { n.Stroke.Id }; selectionButton(sender, e); inkOverlay.Selection = e.Stroke.Ink.CreateStrokes(ids); } e.Stroke.Ink.DeleteStroke(e.Stroke); Invalidate(); return; } //The following code is for pen mode only strokes if (inkOverlay.EditingMode != InkOverlayEditingMode.Ink) { return; } //If a stroke is inside a node, store it in n n = StrokeManager.HitNodeTest(e.Stroke, graph); //If the stroke is closed and it's a start, assign a home or destination if (StrokeManager.isClosed(e.Stroke) && n != null && StrokeManager.isStar(e.Stroke)) { graph.AssignNode(n); RecognizeWeight(); //Attempt at recognizing weight is made after every stroke. } //If the stroke is closed and it is not enclosed in a node and is a circle, make a circular node else if (StrokeManager.isClosed(e.Stroke) && n == null && e.Stroke.PacketCount > StrokeManager.SMALLEST_N_SIZE && StrokeManager.FitsCircleProperties(e.Stroke)) { Stroke circle = StrokeManager.makeCircle(inkOverlay, e.Stroke); Node circleNode = new Node(circle); graph.Add(circleNode); RecognizeWeight(); } //If the stroke is close and it is not enclosed in a node and is a rectangle, make a rectangular node else if (StrokeManager.isClosed(e.Stroke) && n == null && e.Stroke.PacketCount > StrokeManager.SMALLEST_N_SIZE && StrokeManager.FitsRectProperties(e.Stroke)) { Stroke rect = StrokeManager.makeRect(inkOverlay, e.Stroke); Node rectNode = new Node(rect); graph.Add(rectNode); RecognizeWeight(); } //if the stroke isn't closed, then it is an edge. else if (!StrokeManager.isClosed(e.Stroke)) { //Get all the nodes hit by this stroke and create edges for them Nodes edgeNodes = StrokeManager.ifEdgeGetNodes(e.Stroke, graph); if (edgeNodes != null && !StrokeManager.isScratchOut(e.Stroke)) { for (int i = 0; i < edgeNodes.Length() - 1; i++) { if (!Edge.hasEdge(edgeNodes[i], edgeNodes[i + 1])) { Edge edge = new Edge(edgeNodes[i], edgeNodes[i + 1], inkOverlay); graph.Add(edge); } } } else if (StrokeManager.isScratchOut(e.Stroke)) { ArrayList objs = StrokeManager.HitObjects(e.Stroke, graph); for (int i = 0; i < objs.Count; i++) { graph.Remove(objs[i]); } } RecognizeWeight(); } else { //if all of the above fails, then the stroke is considered for edge weights Edge hitEdge = StrokeManager.HitEdgeTest(e.Stroke, graph); if (hitEdge != null) { /* if the edge hit is the same as the previous one, * accumulate strokes for it before recognizing, * if it's a different edge, then recognize and add this * stroke to the new edge. */ if (prevEdgeHit == null) { prevEdgeHit = hitEdge; } if (hitEdge.Equals(prevEdgeHit)) { myRecognizer.Strokes.Add(StrokeManager.CopyStroke(e.Stroke)); } else { RecognizeWeight(); prevEdgeHit = hitEdge; myRecognizer.Strokes.Add(StrokeManager.CopyStroke(e.Stroke)); } } } e.Stroke.Ink.DeleteStroke(e.Stroke); Invalidate(); }