Gray Code מוֹנֶה

התמצאות זו לספריות לוגיסים מסתיימת במונה קוד גריי מתוחכם למדי המאפשר למשתמש לשנות את הערך הנוכחי שלו באמצעות הכלי Poke ולהציב תווית על הרכיב באמצעות הטקסט כְּלִי. זה גם מתאים אישית את הסמל שמופיע בסייר, המשויך לכלי.

GrayCounter

package com.cburch.gray;

import java.net.URL;

import javax.swing.ImageIcon;

import com.cburch.logisim.data.Attribute;
import com.cburch.logisim.data.BitWidth;
import com.cburch.logisim.data.Bounds;
import com.cburch.logisim.data.Direction;
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.util.GraphicsUtil;
import com.cburch.logisim.util.StringUtil;

/** Manufactures a counter that iterates over Gray codes. This demonstrates
 * several additional features beyond the SimpleGrayCounter class. */
class GrayCounter extends InstanceFactory {
    public GrayCounter() {
        super("Gray Counter");
        setOffsetBounds(Bounds.create(-30, -15, 30, 30));
        setPorts(new Port[] {
                new Port(-30, 0, Port.INPUT, 1),
                new Port(  0, 0, Port.OUTPUT, StdAttr.WIDTH),
        });

        // We'll have width, label, and label font attributes. The latter two
        // attributes allow us to associate a label with the component (though
        // we'll also need configureNewInstance to configure the label's
        // location).
        setAttributes(
                new Attribute[] { StdAttr.WIDTH, StdAttr.LABEL, StdAttr.LABEL_FONT },
                new Object[] { BitWidth.create(4), "", StdAttr.DEFAULT_LABEL_FONT });

        // The following method invocation sets things up so that the instance's
        // state can be manipulated using the Poke Tool.
        setInstancePoker(CounterPoker.class);

        // These next two lines set it up so that the explorer window shows a
        // customized icon representing the component type. This should be a
        // 16x16 image.
        URL url = getClass().getClassLoader().getResource("com/cburch/gray/counter.gif");
        if(url != null) setIcon(new ImageIcon(url));
    }

    /** The configureNewInstance method is invoked every time a new instance
     * is created. In the superclass, the method doesn't do anything, since
     * the new instance is pretty thoroughly configured already by default. But
     * sometimes you need to do something particular to each instance, so you
     * would override the method. In this case, we need to set up the location
     * for its label. */
    protected void configureNewInstance(Instance instance) {
        Bounds bds = instance.getBounds();
        instance.setTextField(StdAttr.LABEL, StdAttr.LABEL_FONT,
                bds.getX() + bds.getWidth() / 2, bds.getY() - 3,
                GraphicsUtil.H_CENTER, GraphicsUtil.V_BASELINE);
    }

    public void propagate(InstanceState state) {
        // This is the same as with SimpleGrayCounter, except that we use the
        // StdAttr.WIDTH attribute to determine the bit width to work with.
        BitWidth width = state.getAttributeValue(StdAttr.WIDTH);
        CounterData cur = CounterData.get(state, width);
        boolean trigger = cur.updateClock(state.getPort(0));
        if(trigger) cur.setValue(GrayIncrementer.nextGray(cur.getValue()));
        state.setPort(1, cur.getValue(), 9);
    }

    public void paintInstance(InstancePainter painter) {
        // This is essentially the same as with SimpleGrayCounter, except for
        // the invocation of painter.drawLabel to make the label be drawn.
        painter.drawBounds();
        painter.drawClock(0, Direction.EAST);
        painter.drawPort(1);
        painter.drawLabel();

        if(painter.getShowState()) {
            BitWidth width = painter.getAttributeValue(StdAttr.WIDTH);
            CounterData state = CounterData.get(painter, width);
            Bounds bds = painter.getBounds();
            GraphicsUtil.drawCenteredText(painter.getGraphics(),
                    StringUtil.toHexString(width.getWidth(), state.getValue().toIntValue()),
                    bds.getX() + bds.getWidth() / 2,
                    bds.getY() + bds.getHeight() / 2);
        }
    }
}

CounterPoker

package com.cburch.gray;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;

import com.cburch.logisim.data.BitWidth;
import com.cburch.logisim.data.Bounds;
import com.cburch.logisim.data.Value;
import com.cburch.logisim.instance.InstancePainter;
import com.cburch.logisim.instance.InstancePoker;
import com.cburch.logisim.instance.InstanceState;
import com.cburch.logisim.instance.StdAttr;

/** When the user clicks a counter using the Poke Tool, a CounterPoker object
 * is created, and that object will handle all user events. Note that
 * CounterPoker is a class specific to GrayCounter, and that it must be a
 * subclass of InstancePoker in the com.cburch.logisim.instance package. */
public class CounterPoker extends InstancePoker {
    public CounterPoker() {}

    /** Determines whether the location the mouse was pressed should result
     * in initiating a poke.
     */
    public boolean init(InstanceState state, MouseEvent e) {
        return state.getInstance().getBounds().contains(e.getX(), e.getY());
            // Anywhere in the main rectangle initiates the poke. The user might
            // have clicked within a label, but that will be outside the bounds.
    }

    /** Draws an indicator that the caret is being selected. Here, we'll draw
     * a red rectangle around the value. */
    public void paint(InstancePainter painter) {
        Bounds bds = painter.getBounds();
        BitWidth width = painter.getAttributeValue(StdAttr.WIDTH);
        int len = (width.getWidth() + 3) / 4;

        Graphics g = painter.getGraphics();
        g.setColor(Color.RED);
        int wid = 7 * len + 2; // width of caret rectangle
        int ht = 16; // height of caret rectangle
        g.drawRect(bds.getX() + (bds.getWidth() - wid) / 2,
                bds.getY() + (bds.getHeight() - ht) / 2, wid, ht);
        g.setColor(Color.BLACK);
    }

    /** Processes a key by just adding it onto the end of the current value. */
    public void keyTyped(InstanceState state, KeyEvent e) {
        // convert it to a hex digit; if it isn't a hex digit, abort.
        int val = Character.digit(e.getKeyChar(), 16);
        BitWidth width = state.getAttributeValue(StdAttr.WIDTH);
        if(val < 0 || (val & width.getMask()) != val) return;

        // compute the next value
        CounterData cur = CounterData.get(state, width);
        int newVal = (cur.getValue().toIntValue() * 16 + val) & width.getMask();
        Value newValue = Value.createKnown(width, newVal);
        cur.setValue(newValue);
        state.fireInvalidated();

        // You might be tempted to propagate the value immediately here, using
        // state.setPort. However, the circuit may currently be propagating in
        // another thread, and invoking setPort directly could interfere with
        // that. Using fireInvalidated notifies the propagation thread to
        // invoke propagate on the counter at its next opportunity.
    }
}

Next: Guidelines.