/// <summary> /// Compute the probability that a given set of substrokes has the given type. /// </summary> /// <param name="substrokes"></param> /// <param name="type"></param> /// <param name="featureSketch"></param> /// <returns>a pair containing the recognition probability and the orientation</returns> private RecognitionResult computeRecognitionProbabilityForTextOrWire(SubstrokeCollection substrokes, ShapeType type, FeatureSketch featureSketch) { double probability = 0; double orientation = 0; PhantomShape shape = new PhantomShape(); shape.AddSubstrokes(substrokes); if (LogicDomain.IsWire(type)) { // the probability it is a wire is defined as // (# substrokes classified as wires) / (total # substrokes) int numSubstrokes = substrokes.Count; int numWireSubstrokes = 0; foreach (Substroke substroke in substrokes) { if (_classifications[substroke] == LogicDomain.WIRE_CLASS) { numWireSubstrokes++; } } probability = (double)numWireSubstrokes / numSubstrokes; return(new RecognitionResult(LogicDomain.WIRE, probability, orientation)); } else if (LogicDomain.IsText(type)) { // the probability it is text is defined as // (# substrokes classified as text) / (total # substrokes) int numSubstrokes = substrokes.Count; int numTextSubstrokes = 0; foreach (Substroke substroke in substrokes) { if (_classifications[substroke] == LogicDomain.TEXT_CLASS) { numTextSubstrokes++; } } probability = (double)numTextSubstrokes / numSubstrokes; return(new TextRecognitionResult(probability, _textRecognizer.read(shape))); } return(null); }
/// <summary> /// Creates new EndpointPainters whenever /// a circuit is created /// </summary> public void UpdateEndpoints() { if (!subscribed) { return; } // Reinit // Highlight endpoints foreach (Shape shape in sketchPanel.InkSketch.Sketch.Shapes) { if (LogicDomain.IsWire(shape.Type)) { highlightEndpoints(shape); } } }
/// <summary> /// Highlights the endpoints of a single Wire by /// creating EndPointPainters for all endpoints. /// /// Main function for endpoint highlighting. /// /// Wire must have "AlreadyLabeled" set to true. /// </summary> /// <param name="wire">The Wire to highlight</param> /// <param name="g">The graphics handle to use while creating painters</param> private void highlightEndpoints(Shape wire) { // If this hasn't been recognized or isn't a wire, don not highlight. if (!wire.AlreadyLabeled || !LogicDomain.IsWire(wire.Type)) { return; } List <int> newEppIds = new List <int>(); EndPointPainter epp; int eppId; foreach (Sketch.EndPoint endpoint in wire.Endpoints) { epp = createEndpointPainter(endpoint); eppId = endPointPainterMap.Keys.Count + 1; epp.Id = eppId; newEppIds.Add(eppId); endPointPainterMap.Add(eppId, epp); } // Update stroke map to point to endpoint painters foreach (Sketch.Substroke substroke in wire.Substrokes) { String iStrokeId = sketchPanel.InkSketch.GetInkStrokeIdBySubstrokeId(substroke.XmlAttrs.Id); System.Windows.Ink.Stroke iStroke = sketchPanel.InkSketch.GetInkStrokeById(iStrokeId); if (iStrokeId2EndPtPainterId.ContainsKey(iStroke)) { throw new ApplicationException("Error: encountered one stroke " + "that is part of two or more unique meshes"); } if (iStrokeId != null) { iStrokeId2EndPtPainterId[iStroke] = newEppIds; } } PaintAllEndpoints(); }
/// <summary> /// Connects not bubbles to gates and wires /// </summary> /// <param name="gate"></param> private void connectNotBubble(LogicGate bubble) { LogicGate parentGate = null; WireMesh wire = null; // Get the components this not bubble is connected to foreach (Shape connected in bubble.Shape.ConnectedShapes) { if (LogicDomain.IsGate(connected.Type) && parentGate == null) { parentGate = _logicGates[connected]; } else if (LogicDomain.IsWire(connected.Type)) { wire = _wireMeshes[connected]; } } // If this is not connected to a gate, connect it like a normal logic gate if (parentGate == null) { connectWiresTo(bubble); return; } // Is this bubble on the output or the input? Sketch.EndPoint connectingEndpoint = parentGate.Shape.ClosestEndpointFrom(bubble.Shape); bool isInput = parentGate.ShouldBeInput(connectingEndpoint); if (isInput) { wire.ConnectDependent(bubble); parentGate.ConnectInput(bubble, 0); } else { wire.ConnectSource(bubble, 0); parentGate.ConnectOutput(bubble, 0); } }
/// <summary> /// Checks whether the sketch is valid, and returns a bool indicating this. /// Adds any errors it comes across to the _parseErrors list. /// /// Looks for: /// * Basic consistency via sketch.CheckConsistency /// * Gates connected to only wires /// * Wires not connected to wires /// * Text connected to at most one wire /// </summary> /// <param name="sketch">The sketch to check the validity of</param> /// <returns>A bool, which is true if the sketch is valid</returns> private bool CheckSketch(Sketch.Sketch sketch) { bool valid = true; // Check basic sketch consistency, will (rightly) throw an exception if it finds something wrong. // Eventually we should not need this, for the sketch should always pass this, but it's a useful check for now. sketch.CheckConsistency(); foreach (Sketch.Shape shape in sketch.Shapes) { #region Gate Checks if (LogicDomain.IsGate(shape.Type)) { // Each gate should be connected to only wires foreach (Sketch.Shape connected in shape.ConnectedShapes) { if (!LogicDomain.IsWire(connected.Type)) { valid = false; _parseErrors.Add(new ParseError("Gate " + shape.Name + " is connected to something that is not a wire, " + connected.Name + "of type " + connected.Type.Name, "This gate is connected to something that is not a wire. Draw wires between them or group them together.", shape)); } } } #endregion #region Wire Checks else if (LogicDomain.IsWire(shape.Type)) { // Wires shouldn't be connected to other wires foreach (Sketch.Shape connected in shape.ConnectedShapes) { if (shape != connected && LogicDomain.IsWire(connected.Type)) { valid = false; _parseErrors.Add(new ParseError("Wire " + shape.Name + " is connected to another wire, " + connected.Name, "This wire is connected to another wire. Try grouping these wires together.", shape)); } } } #endregion #region Input/Output Checks else if (LogicDomain.IsText(shape.Type)) { // Text should only be connected to wires, if anything. foreach (Sketch.Shape wire in shape.ConnectedShapes) { if (!LogicDomain.IsWire(wire.Type)) { valid = false; _parseErrors.Add(new ParseError("Text " + shape.Name + " should only be connected to a wire, but is connected to a " + wire.Type.Name + ".", shape.Name + " should only be connected to a wire, but is connected to a " + wire.Type.Name + ".", shape)); } } } #endregion } return(valid); }
/// <summary> /// Determines whether or not a given shape is a circuit /// input, and creates and connects it accordingly. /// Will ignore any text that is not connected to anything. /// /// Assumptions: /// * The names of wire-meshes correspond directly to /// their associated shapes. /// * Wire-meshes all have unique names. /// * The given shape is not a label connected to two wires. /// </summary> /// <param name="shape">The shape to analyze</param> /// <returns>True if loading was successful, false /// if the given shape cannot be recognized as an input /// or output</returns> private void loadInputOrOutput(Sketch.Shape shape) { // Make sure we're actually dealing with something sensical. // Note: If there are no connected shapes, we don't make an input or output, it is just ignored in the circuit. if (shape.Type != LogicDomain.TEXT || shape.ConnectedShapes.Count == 0) { return; } // Retreive the wire connected to this label shape, while // also checking that the shape has the correct number and // kinds of connections. List <WireMesh> connectedWires = new List <WireMesh>(); // Assume that we have an input until we are told otherwise. bool input = true; foreach (Sketch.Shape connectedShape in shape.ConnectedShapes) { // Is this a wire? if (!LogicDomain.IsWire(connectedShape.Type)) { continue; } // Get the connected wire WireMesh connected = _wireMeshes[connectedShape.Name]; // Have we already seen this? if (connectedWires.Contains(connected)) { continue; } connectedWires.Add(connected); // If we're dealing with any wire that already has a source, this will not be an input if (connected.HasSource) { input = false; } } if (input) { CircuitInput newInput = new CircuitInput(shape); foreach (WireMesh wire in connectedWires) { newInput.Connect(wire); } _circuitInputs.Add(newInput.Name, newInput); } else { CircuitOutput newOutput = new CircuitOutput(shape); foreach (WireMesh wire in connectedWires) { newOutput.Connect(wire); } _circuitOutputs.Add(newOutput.Name, newOutput); } }
/// <summary> /// Recalculate the connectedShapes of every shape in a given list, /// and correctly update all related shapes. /// </summary> /// <param name="shapeList">the list of shapes to reconnect</param> /// <param name="featureSketch">the sketch the shapes belong to</param> public void recomputeConnectedShapes(IEnumerable <Sketch.Shape> shapeList, Sketch.Sketch sketch) { // Keep a unique list of shapes HashSet <Sketch.Shape> allRelevantShapes = new HashSet <Sketch.Shape>(shapeList); // Add connected shapes to the list that needs to change foreach (Sketch.Shape shape in shapeList) { foreach (Sketch.Shape connected in shape.ConnectedShapes) { allRelevantShapes.Add(connected); } } // clear ALL connections first foreach (Sketch.Shape shape in allRelevantShapes) { shape.ClearConnections(); } sketch.CheckConsistency(); // connect every shape foreach (Sketch.Shape shape in allRelevantShapes) { connect(shape, sketch); } // Make sure connected wires are the same shape Tuple <Sketch.Shape, Sketch.Shape> pair = null; while ((pair = wiresToMerge(allRelevantShapes)) != null) { sketch.mergeShapes(pair.Item1, pair.Item2); allRelevantShapes.Remove(pair.Item2); sketch.connectShapes(pair.Item1, pair.Item1);; } sketch.CheckConsistency(); #if DEBUG foreach (Shape wire in sketch.Shapes) { if (!LogicDomain.IsWire(wire.Type)) { continue; } foreach (Shape shape in wire.ConnectedShapes) { if (LogicDomain.IsWire(shape.Type) && shape != wire) { throw new Exception("Found two connected wires (" + wire + " and " + shape + ") that were not merged!"); } bool found = false; foreach (EndPoint endpoint in wire.Endpoints) { if (endpoint.ConnectedShape == shape) { found = true; break; } } if (!found) { throw new Exception("The wire " + wire + " is connected to " + shape + " as a connected shape, but not by an endpoint!"); } } } #endif }
/// <summary> /// Connects the given shape to the other shapes in the sketch. /// /// Postcondition: /// If the shape is a wire, the following things are true: /// - For every substroke in the wire, both endpoints are connected to the closest shape /// - The shapes the endpoints are connected to are also in the list of connected shapes /// - The shapes that the wire is connected to also know they are connected to the wire /// If the shape is a NOTBUBBLE /// - It is connected to the two closest shapes?? /// </summary> /// <param name="shape">The shape to check.</param> /// <param name="sketch">The sketch containing the shape.</param> public override void ConnectShape(Sketch.Shape shape, Sketch.Sketch sketch) { if (!sketch.ShapesL.Contains(shape)) { throw new ArgumentException("The given shape " + shape + " is not in the given sketch!"); } // Connect wires to adjacent shapes if (shape.Type == LogicDomain.WIRE) { foreach (Sketch.Substroke substroke in shape.Substrokes) { // Find the shape closest to the start point of the wire Shape shape2 = findClosest(true, substroke, sketch); if (shape2 != null) { EndPoint start = substroke.Endpoints[0]; if (!start.IsConnected) { sketch.connectShapes(shape, shape2); start.ConnectedShape = shape2; } } // Find the shape closest to the end point of the wire Shape shape3 = findClosest(false, substroke, sketch); if (shape3 != null) { EndPoint end = substroke.Endpoints[1]; if (!end.IsConnected) { sketch.connectShapes(shape, shape3); end.ConnectedShape = shape3; } } } } // Connect NotBubbles to its two closest shapes. // FIXME: This could potentially cause problems. For instance, // on the off chance that one of the closest shapes was a wire // whose connections were already figured out, this could leave // the wire in an inconsistent state. else if (shape.Type == LogicDomain.NOTBUBBLE) { List <Sketch.Shape> shapes = twoClosest(shape, sketch); foreach (Shape closeShape in shapes) { if (closeShape == null) { continue; } // FIXME: For now, we're only connecting this notbubble to a wire if that wire's closest endpoint is free. if (closeShape.Type == LogicDomain.WIRE) { EndPoint endpoint = shape.ClosestEndpointFrom(closeShape); if (!endpoint.IsConnected) { closeShape.ConnectNearestEndpointTo(shape); } } else { sketch.connectShapes(closeShape, shape); } } } #if DEBUG foreach (Shape wire in sketch.Shapes) { if (!LogicDomain.IsWire(wire.Type)) { continue; } foreach (Shape connected in wire.ConnectedShapes) { bool found = false; // If a wire is connected to something, then either the wire is // connected by an endpoint to the other shape... foreach (EndPoint endpoint in wire.Endpoints) { if (endpoint.ConnectedShape == connected) { found = true; break; } } // ...or the other shape is connected by an endpoint to the wire foreach (EndPoint endpoint in connected.Endpoints) { if (endpoint.ConnectedShape == wire) { found = true; break; } } if (!found) { throw new Exception("The wire " + wire + " is connected to " + connected + " as a connected shape, but neither has an endpoint connection to the other!"); } } } #endif }