package com.cburch.logisim.std.ttl; import java.awt.Color; import java.awt.Font; import java.awt.Graphics2D; import com.cburch.logisim.data.Attribute; import com.cburch.logisim.data.AttributeSet; import com.cburch.logisim.data.Bounds; import com.cburch.logisim.data.Direction; import com.cburch.logisim.data.Value; 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.prefs.AppPreferences; import com.cburch.logisim.util.GraphicsUtil; public abstract class AbstractTtlGate extends InstanceFactory { protected static final int pinwidth = 10, pinheight = 7, height = 60; protected byte pinnumber; private String name; private byte ngatestodraw = 0; protected String[] portnames = null; private byte[] outputports; /** * @param name = name to display in the center of the TTl * @param pins = the total number of pins (GND and VCC included) * @param outputports = an array with the indexes of the output ports (indexes * are the same you can find on Google searching the TTL you * want to add) **/ protected AbstractTtlGate(String name, byte pins, byte[] outputports) { super(name); setIconName("ttl.gif"); setAttributes( new Attribute[] { StdAttr.FACING, TTL.VCC_GND, TTL.DRAW_INTERNAL_STRUCTURE, StdAttr.LABEL, StdAttr.LABEL_FONT, StdAttr.ATTR_LABEL_COLOR }, new Object[] { Direction.EAST, false, false, "", StdAttr.DEFAULT_LABEL_FONT, Color.BLACK }); setFacingAttribute(StdAttr.FACING); this.name = name; this.pinnumber = pins; this.outputports = outputports; } /** * @param name = name to display in the center of the TTl * @param pins = the total number of pins (GND and VCC included) * @param outputports = an array with the indexes of the output ports (indexes * are the same you can find on Google searching the TTL you * want to add) * @param drawgates = if true, it calls the paintInternal method many times as * the number of output ports passing the coordinates **/ protected AbstractTtlGate(String name, byte pins, byte[] outputports, boolean drawgates) { this(name, pins, outputports); this.ngatestodraw = (byte) (drawgates ? outputports.length : 0); } /** * @param name = name to display in the center of the TTl * @param pins = the total number of pins (GND and VCC included) * @param outputports = an array with the indexes of the output ports (indexes * are the same you can find on Google searching the TTL you * want to add) * @param Ttlportnames = an array of strings which will be tooltips of the * corresponding port in the order you pass **/ protected AbstractTtlGate(String name, byte pins, byte[] outputports, String[] Ttlportnames) { // the ttl name, the total number of pins and an array with the indexes of // output ports (indexes are the one you can find on Google), an array of // strings which will be tooltips of the corresponding port in order this(name, pins, outputports); this.portnames = Ttlportnames; } private void computeTextField(Instance instance) { Bounds bds = instance.getBounds(); Direction dir = instance.getAttributeValue(StdAttr.FACING); if (dir == Direction.EAST || dir == Direction.WEST) instance.setTextField(StdAttr.LABEL, StdAttr.LABEL_FONT, StdAttr.ATTR_LABEL_COLOR, bds.getX() + bds.getWidth() + 3, bds.getY() + bds.getHeight() / 2, GraphicsUtil.H_LEFT, GraphicsUtil.V_CENTER_OVERALL); else instance.setTextField(StdAttr.LABEL, StdAttr.LABEL_FONT, StdAttr.ATTR_LABEL_COLOR, bds.getX() + bds.getWidth() / 2, bds.getY() - 3, GraphicsUtil.H_CENTER, GraphicsUtil.V_CENTER_OVERALL); } @Override protected void configureNewInstance(Instance instance) { instance.addAttributeListener(); updateports(instance); computeTextField(instance); } @Override public Bounds getOffsetBounds(AttributeSet attrs) { Direction dir = attrs.getValue(StdAttr.FACING); return Bounds.create(0, -30, this.pinnumber * 10, height).rotate(Direction.EAST, dir, 0, 0); } @Override protected void instanceAttributeChanged(Instance instance, Attribute<?> attr) { if (attr == StdAttr.FACING) { instance.recomputeBounds(); updateports(instance); computeTextField(instance); } else if (attr == TTL.VCC_GND) { updateports(instance); } } protected void paintBase(InstancePainter painter, boolean drawname, boolean ghost) { Direction dir = painter.getAttributeValue(StdAttr.FACING); Graphics2D g = (Graphics2D) painter.getGraphics(); Bounds bds = painter.getBounds(); int x = bds.getX(); int y = bds.getY(); int xp = x, yp = y; int width = bds.getWidth(); int height = bds.getHeight(); for (byte i = 0; i < this.pinnumber; i++) { if (i < this.pinnumber / 2) { if (dir == Direction.WEST || dir == Direction.EAST) xp = i * 20 + (10 - pinwidth / 2) + x; else yp = i * 20 + (10 - pinwidth / 2) + y; } else { if (dir == Direction.WEST || dir == Direction.EAST) { xp = (i - this.pinnumber / 2) * 20 + (10 - pinwidth / 2) + x; yp = height + y - pinheight; } else { yp = (i - this.pinnumber / 2) * 20 + (10 - pinwidth / 2) + y; xp = width + x - pinheight; } } if (dir == Direction.WEST || dir == Direction.EAST) { // fill the background of white if selected from preferences if (!ghost && AppPreferences.FILL_COMPONENT_BACKGROUND.getBoolean()) { g.setColor(Color.WHITE); g.fillRect(xp, yp, pinwidth, pinheight); g.setColor(Color.BLACK); } g.drawRect(xp, yp, pinwidth, pinheight); } else { // fill the background of white if selected from preferences if (!ghost && AppPreferences.FILL_COMPONENT_BACKGROUND.getBoolean()) { g.setColor(Color.WHITE); g.fillRect(xp, yp, pinheight, pinwidth); g.setColor(Color.BLACK); } g.drawRect(xp, yp, pinheight, pinwidth); } } if (dir == Direction.SOUTH) { // fill the background of white if selected from preferences if (!ghost && AppPreferences.FILL_COMPONENT_BACKGROUND.getBoolean()) { g.setColor(Color.WHITE); g.fillRoundRect(x + pinheight, y, bds.getWidth() - pinheight * 2, bds.getHeight(), 10, 10); g.setColor(Color.BLACK); } g.drawRoundRect(x + pinheight, y, bds.getWidth() - pinheight * 2, bds.getHeight(), 10, 10); g.drawArc(x + width / 2 - 7, y - 7, 14, 14, 180, 180); } else if (dir == Direction.WEST) { // fill the background of white if selected from preferences if (!ghost && AppPreferences.FILL_COMPONENT_BACKGROUND.getBoolean()) { g.setColor(Color.WHITE); g.fillRoundRect(x, y + pinheight, bds.getWidth(), bds.getHeight() - pinheight * 2, 10, 10); g.setColor(Color.BLACK); } g.drawRoundRect(x, y + pinheight, bds.getWidth(), bds.getHeight() - pinheight * 2, 10, 10); g.drawArc(x + width - 7, y + height / 2 - 7, 14, 14, 90, 180); } else if (dir == Direction.NORTH) { // fill the background of white if selected from preferences if (!ghost && AppPreferences.FILL_COMPONENT_BACKGROUND.getBoolean()) { g.setColor(Color.WHITE); g.fillRoundRect(x + pinheight, y, bds.getWidth() - pinheight * 2, bds.getHeight(), 10, 10); g.setColor(Color.BLACK); } g.drawRoundRect(x + pinheight, y, bds.getWidth() - pinheight * 2, bds.getHeight(), 10, 10); g.drawArc(x + width / 2 - 7, y + height - 7, 14, 14, 0, 180); } else {// east // fill the background of white if selected from preferences if (!ghost && AppPreferences.FILL_COMPONENT_BACKGROUND.getBoolean()) { g.setColor(Color.WHITE); g.fillRoundRect(x, y + pinheight, bds.getWidth(), bds.getHeight() - pinheight * 2, 10, 10); g.setColor(Color.BLACK); } g.drawRoundRect(x, y + pinheight, bds.getWidth(), bds.getHeight() - pinheight * 2, 10, 10); g.drawArc(x - 7, y + height / 2 - 7, 14, 14, 270, 180); } g.rotate(Math.toRadians(-dir.toDegrees()), x + width / 2, y + height / 2); if (drawname) { g.setFont(new Font(Font.DIALOG_INPUT, Font.BOLD, 14)); GraphicsUtil.drawCenteredText(g, this.name, x + bds.getWidth() / 2, y + bds.getHeight() / 2 - 4); } if (dir == Direction.WEST || dir == Direction.EAST) { xp = x; yp = y; } else { xp = x + (width - height) / 2; yp = y + (height - width) / 2; width = bds.getHeight(); height = bds.getWidth(); } g.setFont(new Font(Font.DIALOG_INPUT, Font.BOLD, 7)); GraphicsUtil.drawCenteredText(g, "Vcc", xp + 10, yp + pinheight + 4); GraphicsUtil.drawCenteredText(g, "GND", xp + width - 10, yp + height - pinheight - 7); } @Override public void paintGhost(InstancePainter painter) { paintBase(painter, true, true); } @Override public void paintInstance(InstancePainter painter) { painter.drawPorts(); Graphics2D g = (Graphics2D) painter.getGraphics(); painter.drawLabel(); if (!painter.getAttributeValue(TTL.DRAW_INTERNAL_STRUCTURE)) { Direction dir = painter.getAttributeValue(StdAttr.FACING); Bounds bds = painter.getBounds(); int x = bds.getX(); int y = bds.getY(); int xp = x, yp = y; int width = bds.getWidth(); int height = bds.getHeight(); for (byte i = 0; i < this.pinnumber; i++) { if (i == this.pinnumber / 2) { xp = x; yp = y; if (dir == Direction.WEST || dir == Direction.EAST) { g.setColor(Color.DARK_GRAY.darker()); g.fillRoundRect(xp, yp + pinheight, width, height - pinheight * 2 + 2, 10, 10); g.setColor(Color.DARK_GRAY); g.fillRoundRect(xp, yp + pinheight, width, height - pinheight * 2 - 2, 10, 10); g.setColor(Color.BLACK); g.drawRoundRect(xp, yp + pinheight, width, height - pinheight * 2 - 2, 10, 10); g.drawRoundRect(xp, yp + pinheight, width, height - pinheight * 2 + 2, 10, 10); } else { g.setColor(Color.DARK_GRAY.darker()); g.fillRoundRect(xp + pinheight, yp, width - pinheight * 2, height, 10, 10); g.setColor(Color.DARK_GRAY); g.fillRoundRect(xp + pinheight, yp, width - pinheight * 2, height - 4, 10, 10); g.setColor(Color.BLACK); g.drawRoundRect(xp + pinheight, yp, width - pinheight * 2, height - 4, 10, 10); g.drawRoundRect(xp + pinheight, yp, width - pinheight * 2, height, 10, 10); } if (dir == Direction.SOUTH) g.fillArc(xp + width / 2 - 7, yp - 7, 14, 14, 180, 180); else if (dir == Direction.WEST) g.fillArc(xp + width - 7, yp + height / 2 - 7, 14, 14, 90, 180); else if (dir == Direction.NORTH) g.fillArc(xp + width / 2 - 7, yp + height - 11, 14, 14, 0, 180); else // east g.fillArc(xp - 7, yp + height / 2 - 7, 14, 14, 270, 180); } if (i < this.pinnumber / 2) { if (dir == Direction.WEST || dir == Direction.EAST) xp = i * 20 + (10 - pinwidth / 2) + x; else yp = i * 20 + (10 - pinwidth / 2) + y; } else { if (dir == Direction.WEST || dir == Direction.EAST) { xp = (i - this.pinnumber / 2) * 20 + (10 - pinwidth / 2) + x; yp = height + y - pinheight; } else { yp = (i - this.pinnumber / 2) * 20 + (10 - pinwidth / 2) + y; xp = width + x - pinheight; } } if (dir == Direction.WEST || dir == Direction.EAST) { g.setColor(Color.LIGHT_GRAY); g.fillRect(xp, yp, pinwidth, pinheight); g.setColor(Color.BLACK); g.drawRect(xp, yp, pinwidth, pinheight); } else { g.setColor(Color.LIGHT_GRAY); g.fillRect(xp, yp, pinheight, pinwidth); g.setColor(Color.BLACK); g.drawRect(xp, yp, pinheight, pinwidth); } } g.setColor(Color.LIGHT_GRAY.brighter()); g.rotate(Math.toRadians(-dir.toDegrees()), x + width / 2, y + height / 2); g.setFont(new Font(Font.DIALOG_INPUT, Font.BOLD, 14)); GraphicsUtil.drawCenteredText(g, this.name, x + width / 2, y + height / 2 - 4); g.setFont(new Font(Font.DIALOG_INPUT, Font.BOLD, 7)); if (dir == Direction.WEST || dir == Direction.EAST) { xp = x; yp = y; } else { xp = x + (width - height) / 2; yp = y + (height - width) / 2; } if (dir == Direction.SOUTH) { GraphicsUtil.drawCenteredText(g, "Vcc", xp + 10, yp + pinheight + 4); GraphicsUtil.drawCenteredText(g, "GND", xp + height - 14, yp + width - pinheight - 8); } else if (dir == Direction.WEST) { GraphicsUtil.drawCenteredText(g, "Vcc", xp + 10, yp + pinheight + 6); GraphicsUtil.drawCenteredText(g, "GND", xp + width - 10, yp + height - pinheight - 8); } else if (dir == Direction.NORTH) { GraphicsUtil.drawCenteredText(g, "Vcc", xp + 14, yp + pinheight + 4); GraphicsUtil.drawCenteredText(g, "GND", xp + height - 10, yp + width - pinheight - 8); } else { // east GraphicsUtil.drawCenteredText(g, "Vcc", xp + 10, yp + pinheight + 4); GraphicsUtil.drawCenteredText(g, "GND", xp + width - 10, yp + height - pinheight - 10); } } else paintInternalBase(painter); } /** * @param painter = the instance painter you have to use to create Graphics * (Graphics g = painter.getGraphics()) * @param x = if drawgates is false or not used, the component's left * side; if drawgates is true it gets the component's width, * subtracts 20 (for GND or Vcc) and divides for the number of * outputs for each side, you'll get the x coordinate of the * leftmost input -10 before the last output * @param y = the component's upper side * @param height = the component's height * @param up = true if drawgates is true when drawing the gates in the * upper side (introduced this because can't draw upside down so * you have to write what to draw if down and up) **/ abstract public void paintInternal(InstancePainter painter, int x, int y, int height, boolean up); private void paintInternalBase(InstancePainter painter) { Direction dir = painter.getAttributeValue(StdAttr.FACING); Bounds bds = painter.getBounds(); int x = bds.getX(); int y = bds.getY(); int width = bds.getWidth(); int height = bds.getHeight(); if (dir == Direction.SOUTH || dir == Direction.NORTH) { x += (width - height) / 2; y += (height - width) / 2; width = bds.getHeight(); height = bds.getWidth(); } if (this.ngatestodraw == 0) paintInternal(painter, x, y, height, false); else { paintBase(painter, false, false); for (byte i = 0; i < this.ngatestodraw; i++) { paintInternal(painter, x + (i < this.ngatestodraw / 2 ? i : i - this.ngatestodraw / 2) * ((width - 20) / (this.ngatestodraw / 2)) + (i < this.ngatestodraw / 2 ? 0 : 20), y, height, i >= this.ngatestodraw / 2); } } } /** * Here you have to write the logic of your component **/ @Override public void propagate(InstanceState state) { if (state.getAttributeValue(TTL.VCC_GND) && (state.getPort(this.pinnumber - 2) != Value.FALSE || state.getPort(this.pinnumber - 1) != Value.TRUE)) { int port = 0; for (byte i = 0; i < this.outputports.length; i++) { port = this.outputports[i] - (this.outputports[i] >= this.pinnumber / 2 ? 2 : 1); state.setPort(port, Value.UNKNOWN, 1); } } else ttlpropagate(state); } abstract public void ttlpropagate(InstanceState state); private void updateports(Instance instance) { Bounds bds = instance.getBounds(); Direction dir = instance.getAttributeValue(StdAttr.FACING); int dx = 0, dy = 0, width = bds.getWidth(), height = bds.getHeight(); byte portindex = 0; boolean isoutput = false, hasvccgnd = instance.getAttributeValue(TTL.VCC_GND); /* * array port is composed in this order: lower ports less GND, upper ports less * Vcc, GND, Vcc */ Port[] ps = new Port[hasvccgnd ? this.pinnumber : this.pinnumber - 2]; for (byte i = 0; i < this.pinnumber; i++) { for (byte j = 0; j < this.outputports.length; j++) { if (this.outputports[j] == i + 1) isoutput = true; } // set the position if (i < this.pinnumber / 2) { if (dir == Direction.EAST) { dx = i * 20 + 10; dy = 30; } else if (dir == Direction.WEST) { dx = -10 - 20 * i; dy = -30; } else if (dir == Direction.NORTH) { dx = 30; dy = -10 - 20 * i; } else {// SOUTH dx = -30; dy = i * 20 + 10; } } else { if (dir == Direction.EAST) { dx = width - (i - this.pinnumber / 2) * 20 - 10; dy = -30; } else if (dir == Direction.WEST) { dx = -width + (i - this.pinnumber / 2) * 20 + 10; dy = 30; } else if (dir == Direction.NORTH) { dx = -30; dy = -height + (i - this.pinnumber / 2) * 20 + 10; } else {// SOUTH dx = 30; dy = height - (i - this.pinnumber / 2) * 20 - 10; } } // Set the port (output/input) if (isoutput) {// output port ps[portindex] = new Port(dx, dy, Port.OUTPUT, 1); if (this.portnames == null || this.portnames.length <= portindex) ps[portindex].setToolTip(Strings.getter("demultiplexerOutTip", ": " + String.valueOf(i + 1))); else ps[portindex].setToolTip(Strings.getter("demultiplexerOutTip", String.valueOf(i + 1) + ": " + this.portnames[portindex])); } else {// input port if (hasvccgnd && i == this.pinnumber - 1) { // Vcc ps[i] = new Port(dx, dy, Port.INPUT, 1); ps[i].setToolTip(Strings.getter("Vcc: " + this.pinnumber)); } else if (i == this.pinnumber / 2 - 1) {// GND if (hasvccgnd) { ps[ps.length - 2] = new Port(dx, dy, Port.INPUT, 1); ps[ps.length - 2].setToolTip(Strings.getter("GND: " + this.pinnumber / 2)); } portindex--; } else if (i != this.pinnumber - 1 && i != this.pinnumber / 2 - 1) {// normal output ps[portindex] = new Port(dx, dy, Port.INPUT, 1); if (this.portnames == null || this.portnames.length <= portindex) ps[portindex].setToolTip(Strings.getter("multiplexerInTip", ": " + String.valueOf(i + 1))); else ps[portindex].setToolTip(Strings.getter("multiplexerInTip", String.valueOf(i + 1) + ": " + this.portnames[portindex])); } } isoutput = false; portindex++; } instance.setPorts(ps); } }