/******************************************************************************* * This file is part of logisim-evolution. * * logisim-evolution is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * logisim-evolution is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with logisim-evolution. If not, see <http://www.gnu.org/licenses/>. * * Original code by Carl Burch (http://www.cburch.com), 2011. * Subsequent modifications by : * + Haute École Spécialisée Bernoise * http://www.bfh.ch * + Haute École du paysage, d'ingénierie et d'architecture de Genève * http://hepia.hesge.ch/ * + Haute École d'Ingénierie et de Gestion du Canton de Vaud * http://www.heig-vd.ch/ * The project is currently maintained by : * + REDS Institute - HEIG-VD * Yverdon-les-Bains, Switzerland * http://reds.heig-vd.ch *******************************************************************************/ package com.cburch.logisim.std.gates; import java.awt.Color; import java.awt.Graphics; import java.awt.Graphics2D; import javax.swing.Icon; import com.cburch.logisim.comp.ComponentFactory; import com.cburch.logisim.data.Attribute; import com.cburch.logisim.data.AttributeOption; import com.cburch.logisim.data.AttributeSet; import com.cburch.logisim.data.Attributes; import com.cburch.logisim.data.BitWidth; 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.file.Options; import com.cburch.logisim.instance.Instance; import com.cburch.logisim.instance.InstanceFactory; import com.cburch.logisim.instance.InstancePainter; import com.cburch.logisim.instance.InstanceState; import com.cburch.logisim.instance.Port; import com.cburch.logisim.instance.StdAttr; import com.cburch.logisim.tools.WireRepair; import com.cburch.logisim.tools.WireRepairData; import com.cburch.logisim.tools.key.BitWidthConfigurator; import com.cburch.logisim.util.GraphicsUtil; import com.cburch.logisim.util.Icons; class ControlledBuffer extends InstanceFactory { private static final AttributeOption RIGHT_HANDED = new AttributeOption( "right", Strings.getter("controlledRightHanded")); private static final AttributeOption LEFT_HANDED = new AttributeOption( "left", Strings.getter("controlledLeftHanded")); private static final Attribute<AttributeOption> ATTR_CONTROL = Attributes .forOption("control", Strings.getter("controlledControlOption"), new AttributeOption[] { RIGHT_HANDED, LEFT_HANDED }); public static ComponentFactory FACTORY_BUFFER = new ControlledBuffer(false); public static ComponentFactory FACTORY_INVERTER = new ControlledBuffer(true); private static final Icon ICON_BUFFER = Icons .getIcon("controlledBuffer.gif"); private static final Icon ICON_INVERTER = Icons .getIcon("controlledInverter.gif"); private boolean isInverter; private ControlledBuffer(boolean isInverter) { super(isInverter ? "Controlled Inverter" : "Controlled Buffer", isInverter ? Strings.getter("controlledInverterComponent") : Strings.getter("controlledBufferComponent")); this.isInverter = isInverter; if (isInverter) { setAttributes(new Attribute[] { StdAttr.FACING, StdAttr.WIDTH, NotGate.ATTR_SIZE, ATTR_CONTROL, StdAttr.LABEL, StdAttr.LABEL_FONT }, new Object[] { Direction.EAST, BitWidth.ONE, NotGate.SIZE_WIDE, RIGHT_HANDED, "", StdAttr.DEFAULT_LABEL_FONT }); } else { setAttributes(new Attribute[] { StdAttr.FACING, StdAttr.WIDTH, ATTR_CONTROL, StdAttr.LABEL, StdAttr.LABEL_FONT }, new Object[] { Direction.EAST, BitWidth.ONE, RIGHT_HANDED, "", StdAttr.DEFAULT_LABEL_FONT }); } setFacingAttribute(StdAttr.FACING); setKeyConfigurator(new BitWidthConfigurator(StdAttr.WIDTH)); } // // methods for instances // @Override protected void configureNewInstance(Instance instance) { instance.addAttributeListener(); configurePorts(instance); NotGate.configureLabel(instance, false, instance.getPortLocation(2)); } private void configurePorts(Instance instance) { Direction facing = instance.getAttributeValue(StdAttr.FACING); Bounds bds = getOffsetBounds(instance.getAttributeSet()); int d = Math.max(bds.getWidth(), bds.getHeight()) - 20; Location loc0 = Location.create(0, 0); Location loc1 = loc0.translate(facing.reverse(), 20 + d); Location loc2; if (instance.getAttributeValue(ATTR_CONTROL) == LEFT_HANDED) { loc2 = loc0.translate(facing.reverse(), 10 + d, 10); } else { loc2 = loc0.translate(facing.reverse(), 10 + d, -10); } Port[] ports = new Port[3]; ports[0] = new Port(0, 0, Port.OUTPUT, StdAttr.WIDTH); ports[1] = new Port(loc1.getX(), loc1.getY(), Port.INPUT, StdAttr.WIDTH); ports[2] = new Port(loc2.getX(), loc2.getY(), Port.INPUT, 1); instance.setPorts(ports); } @Override public Object getInstanceFeature(final Instance instance, Object key) { if (key == WireRepair.class) { return new WireRepair() { public boolean shouldRepairWire(WireRepairData data) { Location port2 = instance.getPortLocation(2); return data.getPoint().equals(port2); } }; } return super.getInstanceFeature(instance, key); } @Override public Bounds getOffsetBounds(AttributeSet attrs) { int w = 20; if (isInverter && !NotGate.SIZE_NARROW.equals(attrs .getValue(NotGate.ATTR_SIZE))) { w = 30; } Direction facing = attrs.getValue(StdAttr.FACING); if (facing == Direction.NORTH) return Bounds.create(-10, 0, 20, w); if (facing == Direction.SOUTH) return Bounds.create(-10, -w, 20, w); if (facing == Direction.WEST) return Bounds.create(0, -10, w, 20); return Bounds.create(-w, -10, w, 20); } @Override protected void instanceAttributeChanged(Instance instance, Attribute<?> attr) { if (attr == StdAttr.FACING || attr == NotGate.ATTR_SIZE) { instance.recomputeBounds(); configurePorts(instance); NotGate.configureLabel(instance, false, instance.getPortLocation(2)); } else if (attr == ATTR_CONTROL) { configurePorts(instance); NotGate.configureLabel(instance, false, instance.getPortLocation(2)); } } // // graphics methods // @Override public void paintGhost(InstancePainter painter) { paintShape(painter); } @Override public void paintIcon(InstancePainter painter) { Graphics g = painter.getGraphics(); Icon icon = isInverter ? ICON_INVERTER : ICON_BUFFER; if (icon != null) { icon.paintIcon(painter.getDestination(), g, 2, 2); } else { int x = isInverter ? 0 : 2; g.setColor(Color.BLACK); int[] xp = new int[] { x + 15, x + 1, x + 1, x + 15 }; int[] yp = new int[] { 10, 3, 17, 10 }; g.drawPolyline(xp, yp, 4); if (isInverter) g.drawOval(x + 13, 8, 4, 4); g.setColor(Value.FALSE_COLOR); g.drawLine(x + 8, 14, x + 8, 18); } } @Override public void paintInstance(InstancePainter painter) { Direction face = painter.getAttributeValue(StdAttr.FACING); Graphics g = painter.getGraphics(); // draw control wire GraphicsUtil.switchToWidth(g, 3); Location pt0 = painter.getInstance().getPortLocation(2); Location pt1; if (painter.getAttributeValue(ATTR_CONTROL) == LEFT_HANDED) { pt1 = pt0.translate(face, 0, 6); } else { pt1 = pt0.translate(face, 0, -6); } if (painter.getShowState()) { g.setColor(painter.getPortValue(2).getColor()); } g.drawLine(pt0.getX(), pt0.getY(), pt1.getX(), pt1.getY()); // draw triangle g.setColor(Color.BLACK); paintShape(painter); // draw input and output pins if (!painter.isPrintView()) { painter.drawPort(0); painter.drawPort(1); } painter.drawLabel(); } private void paintShape(InstancePainter painter) { Direction facing = painter.getAttributeValue(StdAttr.FACING); Location loc = painter.getLocation(); int x = loc.getX(); int y = loc.getY(); double rotate = 0.0; Graphics g = painter.getGraphics(); g.translate(x, y); if (facing != Direction.EAST && g instanceof Graphics2D) { rotate = -facing.toRadians(); ((Graphics2D) g).rotate(rotate); } if (isInverter) { PainterShaped.paintNot(painter); } else { GraphicsUtil.switchToWidth(g, 2); int d = isInverter ? 10 : 0; int[] xp = new int[] { -d, -19 - d, -19 - d, -d }; int[] yp = new int[] { 0, -7, 7, 0 }; g.drawPolyline(xp, yp, 4); // if (isInverter) g.drawOval(-9, -4, 9, 9); } if (rotate != 0.0) { ((Graphics2D) g).rotate(-rotate); } g.translate(-x, -y); } @Override public void propagate(InstanceState state) { Value control = state.getPortValue(2); BitWidth width = state.getAttributeValue(StdAttr.WIDTH); if (control == Value.TRUE) { Value in = state.getPortValue(1); state.setPort(0, isInverter ? in.not() : in, GateAttributes.DELAY); } else if (control == Value.ERROR || control == Value.UNKNOWN) { state.setPort(0, Value.createError(width), GateAttributes.DELAY); } else { Value out; if (control == Value.UNKNOWN || control == Value.NIL) { AttributeSet opts = state.getProject().getOptions() .getAttributeSet(); if (opts.getValue(Options.ATTR_GATE_UNDEFINED).equals( Options.GATE_UNDEFINED_ERROR)) { out = Value.createError(width); } else { out = Value.createUnknown(width); } } else { out = Value.createUnknown(width); } state.setPort(0, out, GateAttributes.DELAY); } } }