public SketchModification chooseAction(List <SketchModification> availableActions, Sketch.Sketch sketch) { SketchModification result = null; double max = 0; foreach (SketchModification action in availableActions) { double change = action.benefit(); if (change > max) { result = action; max = change; } } return(result); }
public List <SketchModification> SketchModifications(Featurefy.FeatureSketch featureSketch) { Sketch.Sketch sketch = featureSketch.Sketch; if (debug) { Console.WriteLine("Sketch Modifications:"); } // Used to assemble the list of results List <SketchModification> results = new List <SketchModification>(); // Precompute closest contexts for each shape Dictionary <Shape, Tuple <double, ConnectionContext> > closestContexts = new Dictionary <Shape, Tuple <double, ConnectionContext> >(); foreach (Shape shape in sketch.Shapes) { closestContexts.Add(shape, _domain.ClosestContext(shape)); } // ========================================================================================================== /* * Operation zero: running the connector is ALWAYS an option. */ //results.Add(new SketchModification(sketch, new RunConnectorOperation(featureSketch, _connector), computeEnergy)); // ========================================================================================================== /* * First things first: missing connections * This takes care of obvious connector problems. If there is a wire with a dangling endpoint and a * shape that would be better off connected to a wire, we make that connection. The benefit is: * benefit = 1 / distance * where "distance" is the minimum distance from the dangling endpoint to the shape. This will favor * close connections over distant ones. */ List <EndPoint> wiresMissingConnections = findWireEndpointsMissingConnections(sketch); List <Shape> nonWiresMissingWireConnections = findNonWiresMissingConnections(sketch, closestContexts); foreach (EndPoint wireEndpoint in wiresMissingConnections) { foreach (Shape shape in nonWiresMissingWireConnections) { Shape wire = wireEndpoint.ParentShape; if (debug) { Console.WriteLine("ACTION (connect wire endpoint): " + sketch + ", " + wire + ", " + wireEndpoint + ", " + shape); } var op = new ConnectEndpointOperation(sketch, wireEndpoint, shape); var modification = new SketchModification(featureSketch, op, computeEnergy); results.Add(modification); } } // ========================================================================================================== /* * Second: relabeling * Now we go through every shape and see if its context would be better matched as a different shape. * If so, we can change the shape. The benefit is the % improvement in context score plus the % * improvement in recognition quality. */ foreach (Shape shape in sketch.Shapes) { if (shape.AlreadyLabeled) { continue; } Tuple <double, ConnectionContext> currentContext = closestContexts[shape]; List <ShapeType> allTypes = LogicDomain.Types; foreach (ShapeType otherType in AlternateTypes(shape.Type)) { if (debug) { Console.WriteLine("ACTION (relabel shape): " + shape + ", " + otherType); } var op = new RelabelShapeOperation(sketch, shape, RecognizeAsType(shape, otherType, featureSketch)); var modification = new SketchModification(featureSketch, op, computeEnergy); results.Add(modification); } } // ========================================================================================================== /* * Third: stroke steal * This works as follows: * * For every shape, get the set of closeSubstrokes (within a certain threshold). * For every substroke in closeSubstrokes * generate a steal modification (substroke --> shape) * * The steal modifications should have their benefit based on * (1) connection contexts * (2) recognition quality */ foreach (Shape thief in sketch.Shapes) { if (thief.AlreadyLabeled) { continue; } List <Substroke> closeSubstrokes = findSubstrokesCloseTo(thief, sketch, STROKE_STEAL_THRESHOLD); foreach (Substroke gem in closeSubstrokes) // thiefs steal gems { Shape victim = gem.ParentShape; // thiefs steal from victims if (victim.AlreadyLabeled) { continue; } // find the thief's new type var newThiefSubstrokes = new List <Substroke>(thief.SubstrokesL); newThiefSubstrokes.Add(gem); RecognitionResult newThiefRecognition = Identify(newThiefSubstrokes, featureSketch); if (debug) { Console.WriteLine("ACTION (steal stroke): " + thief + ", " + victim + ", " + gem); } var stealOp = new StrokeStealOperation(sketch, thief, gem); var relabelThiefOp = new RelabelShapeOperation(sketch, thief, newThiefRecognition); var runConnectorOp = new RunConnectorOperation(featureSketch, _connector); ISketchOperation op; // if the victim will still be around after the steal if (victim.Substrokes.Length > 1) { var newVictimSubstrokes = new List <Substroke>(victim.SubstrokesL); newVictimSubstrokes.Remove(gem); RecognitionResult newVictimRecognition = Identify(newVictimSubstrokes, featureSketch); var relabelVictimOp = new RelabelShapeOperation(sketch, victim, newVictimRecognition); op = new CompoundSketchOperation(stealOp, relabelThiefOp, relabelVictimOp, runConnectorOp); } else { op = new CompoundSketchOperation(stealOp, relabelThiefOp, runConnectorOp); } var modification = new SketchModification(featureSketch, op, computeEnergy); results.Add(modification); } } if (debug && results.Count == 0) { Console.WriteLine("(none)"); } // Keep only the ones greater than the cutoff results = results.FindAll(r => { return(r.benefit() > BENEFIT_CUTOFF); }); return(results); }