Package com.cburch.logisim.std.gates

Source Code of com.cburch.logisim.std.gates.CircuitBuilder$CompareYs

/* Copyright (c) 2010, Carl Burch. License information is located in the
* com.cburch.logisim.Main source code and at www.cburch.com/logisim/. */

package com.cburch.logisim.std.gates;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;

import com.cburch.logisim.analyze.model.AnalyzerModel;
import com.cburch.logisim.analyze.model.Expression;
import com.cburch.logisim.analyze.model.VariableList;
import com.cburch.logisim.circuit.Circuit;
import com.cburch.logisim.circuit.CircuitMutation;
import com.cburch.logisim.circuit.Wire;
import com.cburch.logisim.comp.Component;
import com.cburch.logisim.comp.ComponentFactory;
import com.cburch.logisim.data.AttributeSet;
import com.cburch.logisim.data.Bounds;
import com.cburch.logisim.data.Direction;
import com.cburch.logisim.data.Location;
import com.cburch.logisim.data.Value;
import com.cburch.logisim.instance.StdAttr;
import com.cburch.logisim.std.wiring.Constant;
import com.cburch.logisim.std.wiring.Pin;

public class CircuitBuilder {
  private CircuitBuilder() { }
 
  public static CircuitMutation build(Circuit destCirc, AnalyzerModel model,
      boolean twoInputs, boolean useNands) {
    CircuitMutation result = new CircuitMutation(destCirc);
    result.clear();
   
    Layout[] layouts = new Layout[model.getOutputs().size()];
    int maxWidth = 0;
    for (int i = 0; i < layouts.length; i++) {
      String output = model.getOutputs().get(i);
      Expression expr = model.getOutputExpressions().getExpression(output);
      CircuitDetermination det = CircuitDetermination.create(expr);
      if (det != null) {
        if (twoInputs) det.convertToTwoInputs();
        if (useNands) det.convertToNands();
        det.repair();
        layouts[i] = layoutGates(det);
        maxWidth = Math.max(maxWidth, layouts[i].width);
      } else {
        layouts[i] = null;
      }
    }
   
    InputData inputData = computeInputData(model);
    int x = inputData.getStartX();
    int y = 10;
    int outputX = x + maxWidth + 20;
    for (int i = 0; i < layouts.length; i++) {
      String outputName = model.getOutputs().get(i);
      Layout layout = layouts[i];
      Location output;
      int height;
      if (layout == null) {
        output = Location.create(outputX, y + 20);
        height = 40;
      } else {
        int dy = 0;
        if (layout.outputY < 20) dy = 20 - layout.outputY;
        height = Math.max(dy + layout.height, 40);
        output = Location.create(outputX, y + dy + layout.outputY);
        placeComponents(result, layouts[i], x, y + dy, inputData, output);
      }
      placeOutput(result, output, outputName);
      y += height + 10;
    }
    placeInputs(result, inputData);
    return result;
  }

  //
  // layoutGates
  //
  private static Layout layoutGates(CircuitDetermination det) {
    return layoutGatesSub(det);
  }
 
  private static class Layout {
    // initialized by parent
    int y; // top edge relative to parent's top edge
       // (or edge corresponding to input)
   
    // initialized by self
    int width;
    int height;
    ComponentFactory factory;
    AttributeSet attrs;
    int outputY; // where output is relative to my top edge
    int subX; // where right edge of sublayouts should be relative to my left edge
    Layout[] subLayouts;
    String inputName; // for references directly to inputs

    Layout(int width, int height, int outputY,
        ComponentFactory factory, AttributeSet attrs,
        Layout[] subLayouts, int subX) {
      this.width = width;
      this.height = roundUp(height);
      this.outputY = outputY;
      this.factory = factory;
      this.attrs = attrs;
      this.subLayouts = subLayouts;
      this.subX = subX;
      this.inputName = null;
    }
   
    Layout(String inputName) {
      this(0, 0, 0, null, null, null, 0);
      this.inputName = inputName;
    }
  }
 
  private static Layout layoutGatesSub(CircuitDetermination det) {
    if (det instanceof CircuitDetermination.Input) {
      CircuitDetermination.Input input = (CircuitDetermination.Input) det;
      return new Layout(input.getName());
    } else if (det instanceof CircuitDetermination.Value) {
      CircuitDetermination.Value value = (CircuitDetermination.Value) det;
      ComponentFactory factory = Constant.FACTORY;
      AttributeSet attrs = factory.createAttributeSet();
      attrs.setValue(Constant.ATTR_VALUE,
          Integer.valueOf(value.getValue()));
      Bounds bds = factory.getOffsetBounds(attrs);
      return new Layout(bds.getWidth(), bds.getHeight(),
          -bds.getY(), factory, attrs,
          new Layout[0], 0);
    }
   
    // We know det is a Gate. Determine sublayouts.
    CircuitDetermination.Gate gate = (CircuitDetermination.Gate) det;
    ComponentFactory factory = gate.getFactory();
    ArrayList<CircuitDetermination> inputs = gate.getInputs();
   
    // Handle a NOT implemented with a NAND as a special case
    if (gate.isNandNot()) {
      CircuitDetermination subDet = inputs.get(0);
      if (!(subDet instanceof CircuitDetermination.Input)) {
        Layout[] sub = new Layout[1];
        sub[0] = layoutGatesSub(subDet);
        sub[0].y = 0;
       
        AttributeSet attrs = factory.createAttributeSet();
        attrs.setValue(GateAttributes.ATTR_SIZE, GateAttributes.SIZE_NARROW);
        attrs.setValue(GateAttributes.ATTR_INPUTS, Integer.valueOf(2));
 
        // determine layout's width
        Bounds bds = factory.getOffsetBounds(attrs);
        int betweenWidth = 40;
        if (sub[0].width == 0) betweenWidth = 0;
        int width = sub[0].width + betweenWidth + bds.getWidth();
       
        // determine outputY and layout's height.
        int outputY = sub[0].y + sub[0].outputY;
        int height = sub[0].height;
        int minOutputY = roundUp(-bds.getY());
        if (minOutputY > outputY) {
          // we have to shift everything down because otherwise
          // the component will peek over the rectangle's top.
          int dy = minOutputY - outputY;
          sub[0].y += dy;
          height += dy;
          outputY += dy;
        }
        int minHeight = outputY + bds.getY() + bds.getHeight();
        if (minHeight > height) height = minHeight;
       
        // ok; create and return the layout.
        return new Layout(width, height, outputY, factory, attrs,
            sub, sub[0].width);
      }
    }
   
    Layout[] sub = new Layout[inputs.size()];
    int subWidth = 0; // maximum width of sublayouts
    int subHeight = 0; // total height of sublayouts
    for (int i = 0; i < sub.length; i++) {
      sub[i] = layoutGatesSub(inputs.get(i));
      if (sub.length % 2 == 0 && i == (sub.length + 1) / 2
          && sub[i - 1].height + sub[i].height == 0) {
        // if there are an even number of inputs, then there is a
        // 20-tall gap between the middle two inputs. Ensure the two
        // middle inputs are at least 20 pixels apart.
        subHeight += 10;
      }
      sub[i].y = subHeight;
      subWidth = Math.max(subWidth, sub[i].width);
      subHeight += sub[i].height + 10;
    }
    subHeight -= 10;
   
    AttributeSet attrs = factory.createAttributeSet();
    if (factory == NotGate.FACTORY) {
      attrs.setValue(NotGate.ATTR_SIZE, NotGate.SIZE_NARROW);
    } else {
      attrs.setValue(GateAttributes.ATTR_SIZE, GateAttributes.SIZE_NARROW);
     
      int ins = sub.length;
      attrs.setValue(GateAttributes.ATTR_INPUTS, Integer.valueOf(ins));
    }

    // determine layout's width
    Bounds bds = factory.getOffsetBounds(attrs);
    int betweenWidth = 40 + 10 * (sub.length / 2 - 1);
    if (sub.length == 1) betweenWidth = 20;
    if (subWidth == 0) betweenWidth = 0;
    int width = subWidth + betweenWidth + bds.getWidth();
   
    // determine outputY and layout's height.
    int outputY;
    if (sub.length % 2 == 1) { // odd number - match the middle input
      int i = (sub.length - 1) / 2;
      outputY = sub[i].y + sub[i].outputY;
    } else { // even number - halfway between middle two inputs
      int i0 = (sub.length / 2) - 1;
      int i1 = (sub.length / 2);
      int o0 = sub[i0].y + sub[i0].outputY;
      int o1 = sub[i1].y + sub[i1].outputY;
      outputY = roundDown((o0 + o1) / 2);
    }
    int height = subHeight;
    int minOutputY = roundUp(-bds.getY());
    if (minOutputY > outputY) {
      // we have to shift everything down because otherwise
      // the component will peek over the rectangle's top.
      int dy = minOutputY - outputY;
      for (int i = 0; i < sub.length; i++) sub[i].y += dy;
      height += dy;
      outputY += dy;
    }
    int minHeight = outputY + bds.getY() + bds.getHeight();
    if (minHeight > height) height = minHeight;
   
    // ok; create and return the layout.
    return new Layout(width, height, outputY, factory, attrs,
        sub, subWidth);
  }
 
  private static int roundDown(int value) {
    return value / 10 * 10;
  }
 
  private static int roundUp(int value) {
    return (value + 9) / 10 * 10;
  }
 
  //
  // computeInputData
  //
  private static InputData computeInputData(AnalyzerModel model) {
    InputData ret = new InputData();
    VariableList inputs = model.getInputs();
    int spineX = 60;
    ret.names = new String[inputs.size()];
    for (int i = 0; i < inputs.size(); i++) {
      String name = inputs.get(i);
      ret.names[i] = name;
      ret.inputs.put(name, new SingleInput(spineX));
      spineX += 20;
    }
    ret.startX = spineX;
    return ret;
  }
 
  private static class InputData {
    int startX;
    String[] names;
    HashMap<String,SingleInput> inputs = new HashMap<String,SingleInput>();
   
    InputData() { }
   
    int getStartX() {
      return startX;
    }
   
    int getSpineX(String input) {
      SingleInput data = inputs.get(input);
      return data.spineX;
    }
   
    void registerConnection(String input, Location loc) {
      SingleInput data = inputs.get(input);
      data.ys.add(loc);
    }
  }
   
  private static class SingleInput {
    int spineX;
    ArrayList<Location> ys = new ArrayList<Location>();
   
    SingleInput(int spineX) { this.spineX = spineX; }
  }
 
  //
  // placeComponents
  //
  /**
   * @param circuit  the circuit where to place the components.
   * @param layout   the layout specifying the gates to place there.
   * @param x        the left edge of where the layout should be placed.
   * @param y        the top edge of where the layout should be placed.
   * @param inputData  information about how to reach inputs.
   * @param output   a point to which the output should be connected.
   */
  private static void placeComponents(CircuitMutation result,
      Layout layout, int x, int y, InputData inputData,
      Location output) {
    if (layout.inputName != null) {
      int inputX = inputData.getSpineX(layout.inputName);
      Location input = Location.create(inputX, output.getY());
      inputData.registerConnection(layout.inputName, input);
      result.add(Wire.create(input, output));
      return;
    }

    Location compOutput = Location.create(x + layout.width, output.getY());
    Component parent = layout.factory.createComponent(compOutput,
        layout.attrs);
    result.add(parent);
    if (!compOutput.equals(output)) {
      result.add(Wire.create(compOutput, output));
    }
   
    // handle a NOT gate pattern implemented with NAND as a special case
    if (layout.factory == NandGate.FACTORY && layout.subLayouts.length == 1
        && layout.subLayouts[0].inputName == null) {
      Layout sub = layout.subLayouts[0];
     
      Location input0 = parent.getEnd(1).getLocation();
      Location input1 = parent.getEnd(2).getLocation();
     
      int midX = input0.getX() - 20;
      Location subOutput = Location.create(midX, output.getY());
      Location midInput0 = Location.create(midX, input0.getY());
      Location midInput1 = Location.create(midX, input1.getY());
      result.add(Wire.create(subOutput, midInput0));
      result.add(Wire.create(midInput0, input0));
      result.add(Wire.create(subOutput, midInput1));
      result.add(Wire.create(midInput1, input1));
     
      int subX = x + layout.subX - sub.width; 
      placeComponents(result, sub, subX, y + sub.y, inputData, subOutput);
      return;
    }
   
    if (layout.subLayouts.length == parent.getEnds().size() - 2) {
      int index = layout.subLayouts.length / 2 + 1;
      Object factory = parent.getFactory();
      if (factory instanceof AbstractGate) {
        Value val = ((AbstractGate) factory).getIdentity();
        Integer valInt = Integer.valueOf(val.toIntValue());
        Location loc = parent.getEnd(index).getLocation();
        AttributeSet attrs = Constant.FACTORY.createAttributeSet();
        attrs.setValue(Constant.ATTR_VALUE, valInt);
        result.add(Constant.FACTORY.createComponent(loc, attrs));
      }
    }

    for (int i = 0; i < layout.subLayouts.length; i++) {
      Layout sub = layout.subLayouts[i];
     
      int inputIndex = i + 1;
      Location subDest = parent.getEnd(inputIndex).getLocation();
     
      int subOutputY = y + sub.y + sub.outputY;
      if (sub.inputName != null) {
        int destY = subDest.getY();
        if (i == 0 && destY < subOutputY
            || i == layout.subLayouts.length - 1 && destY > subOutputY) {
          subOutputY = destY;
        }
      }
     
      Location subOutput;
      int numSubs = layout.subLayouts.length;
      if (subOutputY == subDest.getY()) {
        subOutput = subDest;
      } else {
        int back;
        if (i < numSubs / 2) {
          if (subOutputY < subDest.getY()) { // bending upward
            back = i;
          } else {
            back = ((numSubs - 1) / 2) - i;
          }
        } else {
          if (subOutputY > subDest.getY()) { // bending downward
            back = numSubs - 1 - i;
          } else {
            back = i - (numSubs / 2);
          }
        }
        int subOutputX = subDest.getX() - 20 - 10 * back;
        subOutput = Location.create(subOutputX, subOutputY);
        Location mid = Location.create(subOutputX, subDest.getY());
        result.add(Wire.create(subOutput, mid));
        result.add(Wire.create(mid, subDest));
      }
       
      int subX = x + layout.subX - sub.width;
      int subY = y + sub.y;
      placeComponents(result, sub, subX, subY, inputData, subOutput);
    }
  }
 
  //
  // placeOutput
  //
  private static void placeOutput(CircuitMutation result, Location loc, String name) {
    ComponentFactory factory = Pin.FACTORY;
    AttributeSet attrs = factory.createAttributeSet();
    attrs.setValue(StdAttr.FACING, Direction.WEST);
    attrs.setValue(Pin.ATTR_TYPE, Boolean.TRUE);
    attrs.setValue(StdAttr.LABEL, name);
    attrs.setValue(Pin.ATTR_LABEL_LOC, Direction.NORTH);
    result.add(factory.createComponent(loc, attrs));
  }
 
  //
  // placeInputs
  //
  private static void placeInputs(CircuitMutation result, InputData inputData) {
    ArrayList<Location> forbiddenYs = new ArrayList<Location>();
    Comparator<Location> compareYs = new CompareYs();
    int curX = 40;
    int curY = 30;
    for (int i = 0; i < inputData.names.length; i++) {
      String name = inputData.names[i];
      SingleInput singleInput = inputData.inputs.get(name);
     
      // determine point where we can intersect with spine
      int spineX = singleInput.spineX;
      Location spineLoc = Location.create(spineX, curY);
      if (singleInput.ys.size() > 0) {
        // search for a Y that won't intersect with others
        // (we needn't bother if the pin doesn't connect
        // with anything anyway.)
        Collections.sort(forbiddenYs, compareYs);
        while (Collections.binarySearch(forbiddenYs, spineLoc, compareYs) >= 0) {
          curY += 10;
          spineLoc = Location.create(spineX, curY);
        }
        singleInput.ys.add(spineLoc);
      }
      Location loc = Location.create(curX, curY);
     
      // now create the pin
      ComponentFactory factory = Pin.FACTORY;
      AttributeSet attrs = factory.createAttributeSet();
      attrs.setValue(StdAttr.FACING, Direction.EAST);
      attrs.setValue(Pin.ATTR_TYPE, Boolean.FALSE);
      attrs.setValue(Pin.ATTR_TRISTATE, Boolean.FALSE);
      attrs.setValue(StdAttr.LABEL, name);
      attrs.setValue(Pin.ATTR_LABEL_LOC, Direction.NORTH);
      result.add(factory.createComponent(loc, attrs));
     
      ArrayList<Location> spine = singleInput.ys;
      if (spine.size() > 0) {
        // create wire connecting pin to spine
        /* This should no longer matter - the wires will be repaired
         * anyway by the circuit's WireRepair class.
        if (spine.size() == 2 && spine.get(0).equals(spine.get(1))) {
          // a freak accident where the input is used just once,
          // and it happens that the pin is placed where no
          // spine is necessary
          Iterator<Wire> it = circuit.getWires(spineLoc).iterator();
          Wire existing = it.next();
          Wire replace = Wire.create(loc, existing.getEnd1());
          result.replace(existing, replace);
        } else {
         */
          result.add(Wire.create(loc, spineLoc));
        // }

        // create spine
        Collections.sort(spine, compareYs);
        Location prev = spine.get(0);
        for (int k = 1, n = spine.size(); k < n; k++) {
          Location cur = spine.get(k);
          if (!cur.equals(prev)) {
            result.add(Wire.create(prev, cur));
            prev = cur;
          }
        }
      }
         
      // advance y and forbid spine intersections for next pin
      forbiddenYs.addAll(singleInput.ys);
      curY += 50;
    }
  }
 
  private static class CompareYs implements Comparator<Location> {
    public int compare(Location a, Location b) {
      return a.getY() - b.getY();
    }
  }
}
TOP

Related Classes of com.cburch.logisim.std.gates.CircuitBuilder$CompareYs

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.