/// <summary> /// Constructs a Circuit given a Dictionary of all the gates, /// List of all outputs, and List of all inputs /// </summary> public Circuit(Dictionary <Sketch.Shape, Dictionary <int, Tuple <Shape, int> > > shapesAndTheirInputs, List <Shape> outputs, List <Shape> inputs, Dictionary <Sketch.Shape, CircuitElement> subCircuits) : this() { // Build global inputs foreach (Shape inputShape in inputs) { INPUT input = new INPUT(inputShape.Name, inputShape.Bounds, inputShape.Orientation); shapesToElements.Add(inputShape, input); globalInputs.Add(input); } // Build global outputs foreach (Shape outputShape in outputs) { OUTPUT output = new OUTPUT(outputShape.Name, outputShape.Bounds); shapesToElements.Add(outputShape, output); globalOutputs.Add(output); } // Build logic gates within the circuit foreach (var shapeAndInputs in shapesAndTheirInputs) { Shape gateShape = shapeAndInputs.Key; Gate gateElement; // If the shape is not a gate, or is already in the circuit, skip it. if (!LogicDomain.IsGate(gateShape.Type) || shapesToElements.ContainsKey(gateShape)) { continue; } // If it's a subcircuit, roll our own circuit element. else if (subCircuits.Keys.Contains(gateShape)) { SubCircuit associatedSub = (SubCircuit)subCircuits[gateShape]; associatedSub = new SubCircuit(gateShape.Name, gateShape.Bounds, gateShape, associatedSub.inputs, associatedSub.outputs, associatedSub.behavior, gateShape.Orientation); gateElement = associatedSub; subCircuits[gateShape] = associatedSub; } // Otherwise, create an element normally. else { gateElement = createGate(gateShape.Name, gateShape.Bounds, gateShape.Orientation, gateShape.Type); } if (gateElement == null) { throw new Exception("failed to create gate!"); } shapesToElements.Add(gateShape, gateElement); gates.Add(gateElement); } connectEverything(shapesAndTheirInputs); }
/// <summary> /// Returns a pair of the minimum (first) and maximum (second) number of outputs that this shape can have. /// </summary> /// <param name="shape"></param> /// <returns></returns> public IntRange NumberOutputs(ShapeType shape) { if (shape == LogicDomain.SUBCIRCUIT) { return(new IntRange(1, int.MaxValue)); // we check validity in checkSubcircuits() } else if (LogicDomain.IsGate(shape)) { return(new IntRange(1, 1)); } return(new IntRange(1, int.MaxValue)); }
/// <summary> /// Runs the tests! /// </summary> /// <param name="sketch"></param> /// <param name="filename"></param> public override void run(Sketch.Sketch sketch, string filename) { // store the first filename as a way to tell what user we're working on. if (_filename == null) { _filename = filename; } Dictionary <ShapeType, MutablePair <int, int> > sketchResults = new Dictionary <ShapeType, MutablePair <int, int> >(); foreach (ShapeType type in LogicDomain.Gates) { sketchResults.Add(type, MutablePair.Create(0, 0)); } Sketch.Sketch handLabeled = sketch.Clone(); _pipeline.process(sketch); foreach (Sketch.Shape correctShape in handLabeled.Shapes) { if (!LogicDomain.IsGate(correctShape.Type)) { continue; } Sketch.Shape resultShape = sketch.ShapesL.Find(delegate(Sketch.Shape s) { return(s.GeometricEquals(correctShape)); }); if (resultShape == null) { throw new Exception("Could not find shape."); } sketchResults[correctShape.Type].Item1++; if (resultShape.Type == correctShape.Type) { sketchResults[correctShape.Type].Item2++; } } _results.Add(sketchResults); }
/// <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> /// Compute the energy function that we are trying to maximize. /// </summary> /// <param name="sketch"></param> /// <returns>an unbounded double representing the energy of the sketch</returns> private double computeEnergy(FeatureSketch featureSketch) { Sketch.Sketch sketch = featureSketch.Sketch; featureSketch.hasConsistentSubstrokes(); sketch.CheckConsistency(); double energy = 0; var shapes = sketch.ShapesL; int numShapes = shapes.Count; int numGates = shapes.FindAll(s => LogicDomain.IsGate(s.Type)).Count; foreach (Shape shape in shapes) { // Add energy for every connection #if false // This is a bad idea. It favors interpretations with more connections, which basically means // that everything should alternate wire-text-wire-text-wire-... foreach (Shape connected in shape.ConnectedShapes) { if (connected == shape) { continue; } if (connected.Type != LogicDomain.WIRE) { continue; } double connectionDistance = double.PositiveInfinity; foreach (EndPoint endpoint in connected.Endpoints) { connectionDistance = Math.Min(shape.minDistanceTo(endpoint.X, endpoint.Y), connectionDistance); } connectionDistance = Math.Max(connectionDistance, 0.001); // avoid problems when connection distance is close to zero energy += 1 + 1 / connectionDistance; } #endif // Add the context match score energy += (double)_domain.ClosestContext(shape).Item1 / numShapes; // Get recognition results RecognitionResult result = RecognizeAsType(shape, shape.Type, featureSketch); double confidence = result.Confidence; // Add the recognition score energy += confidence / numShapes; #if false // Gate orientation also contributes if (LogicDomain.IsGate(shape.Type)) { // Determine the recognizer's and the connector's orientation values, // in the range [0, 2Pi] double orientation = result.Orientation; double otherOrientation = _domain.OrientShape(shape, sketch); // Orientation might be off by PI... double dist1 = Math.Abs(otherOrientation - orientation); double dist2 = Math.Abs(otherOrientation - Math.PI - orientation); double dist = Math.Min(dist1, dist2); // Add orientation score double twoPI = 2 * Math.PI; energy += (1 - (dist / twoPI)) / numGates; } #endif } return(energy); }
/// <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); }