/*******************************************************************************
* 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.io;
import java.awt.Color;
import java.awt.Graphics;
import java.util.Arrays;
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.Bounds;
import com.cburch.logisim.data.Value;
import com.cburch.logisim.instance.Instance;
import com.cburch.logisim.instance.InstanceData;
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.std.wiring.DurationAttribute;
import com.cburch.logisim.util.GraphicsUtil;
// TODO repropagate when rows/cols change
public class DotMatrix extends InstanceFactory {
private static class State implements InstanceData, Cloneable {
private int rows;
private int cols;
private Value[] grid;
private long[] persistTo;
public State(int rows, int cols, long curClock) {
this.rows = -1;
this.cols = -1;
updateSize(rows, cols, curClock);
}
@Override
public Object clone() {
try {
State ret = (State) super.clone();
ret.grid = this.grid.clone();
ret.persistTo = this.persistTo.clone();
return ret;
} catch (CloneNotSupportedException e) {
return null;
}
}
private Value get(int row, int col, long curTick) {
int index = row * cols + col;
Value ret = grid[index];
if (ret == Value.FALSE && persistTo[index] - curTick >= 0) {
ret = Value.TRUE;
}
return ret;
}
private void setColumn(int index, Value colVector, long persist) {
int gridloc = (rows - 1) * cols + index;
int stride = -cols;
Value[] vals = colVector.getAll();
for (int i = 0; i < vals.length; i++, gridloc += stride) {
Value val = vals[i];
if (grid[gridloc] == Value.TRUE) {
persistTo[gridloc] = persist - 1;
}
grid[gridloc] = val;
if (val == Value.TRUE) {
persistTo[gridloc] = persist;
}
}
}
private void setRow(int index, Value rowVector, long persist) {
int gridloc = (index + 1) * cols - 1;
int stride = -1;
Value[] vals = rowVector.getAll();
for (int i = 0; i < vals.length; i++, gridloc += stride) {
Value val = vals[i];
if (grid[gridloc] == Value.TRUE) {
persistTo[gridloc] = persist - 1;
}
grid[gridloc] = vals[i];
if (val == Value.TRUE) {
persistTo[gridloc] = persist;
}
}
}
private void setSelect(Value rowVector, Value colVector, long persist) {
Value[] rowVals = rowVector.getAll();
Value[] colVals = colVector.getAll();
int gridloc = 0;
for (int i = rowVals.length - 1; i >= 0; i--) {
Value wholeRow = rowVals[i];
if (wholeRow == Value.TRUE) {
for (int j = colVals.length - 1; j >= 0; j--, gridloc++) {
Value val = colVals[colVals.length - 1 - j];
if (grid[gridloc] == Value.TRUE) {
persistTo[gridloc] = persist - 1;
}
grid[gridloc] = val;
if (val == Value.TRUE) {
persistTo[gridloc] = persist;
}
}
} else {
if (wholeRow != Value.FALSE)
wholeRow = Value.ERROR;
for (int j = colVals.length - 1; j >= 0; j--, gridloc++) {
if (grid[gridloc] == Value.TRUE) {
persistTo[gridloc] = persist - 1;
}
grid[gridloc] = wholeRow;
}
}
}
}
private void updateSize(int rows, int cols, long curClock) {
if (this.rows != rows || this.cols != cols) {
this.rows = rows;
this.cols = cols;
int length = rows * cols;
grid = new Value[length];
persistTo = new long[length];
Arrays.fill(grid, Value.UNKNOWN);
Arrays.fill(persistTo, curClock - 1);
}
}
}
static final AttributeOption INPUT_SELECT = new AttributeOption("select",
Strings.getter("ioInputSelect"));
static final AttributeOption INPUT_COLUMN = new AttributeOption("column",
Strings.getter("ioInputColumn"));
static final AttributeOption INPUT_ROW = new AttributeOption("row",
Strings.getter("ioInputRow"));
static final AttributeOption SHAPE_CIRCLE = new AttributeOption("circle",
Strings.getter("ioShapeCircle"));
static final AttributeOption SHAPE_SQUARE = new AttributeOption("square",
Strings.getter("ioShapeSquare"));
static final Attribute<AttributeOption> ATTR_INPUT_TYPE = Attributes
.forOption("inputtype", Strings.getter("ioMatrixInput"),
new AttributeOption[] { INPUT_COLUMN, INPUT_ROW,
INPUT_SELECT });
static final Attribute<Integer> ATTR_MATRIX_COLS = Attributes
.forIntegerRange("matrixcols", Strings.getter("ioMatrixCols"), 1,
Value.MAX_WIDTH);
static final Attribute<Integer> ATTR_MATRIX_ROWS = Attributes
.forIntegerRange("matrixrows", Strings.getter("ioMatrixRows"), 1,
Value.MAX_WIDTH);
static final Attribute<AttributeOption> ATTR_DOT_SHAPE = Attributes
.forOption("dotshape", Strings.getter("ioMatrixShape"),
new AttributeOption[] { SHAPE_CIRCLE, SHAPE_SQUARE });
static final Attribute<Integer> ATTR_PERSIST = new DurationAttribute(
"persist", Strings.getter("ioMatrixPersistenceAttr"), 0,
Integer.MAX_VALUE);
public DotMatrix() {
super("DotMatrix", Strings.getter("dotMatrixComponent"));
setAttributes(new Attribute<?>[] { ATTR_INPUT_TYPE, ATTR_MATRIX_COLS,
ATTR_MATRIX_ROWS, Io.ATTR_ON_COLOR, Io.ATTR_OFF_COLOR,
ATTR_PERSIST, ATTR_DOT_SHAPE }, new Object[] { INPUT_COLUMN,
Integer.valueOf(5), Integer.valueOf(7), Color.GREEN,
Color.DARK_GRAY, Integer.valueOf(0), SHAPE_SQUARE });
setIconName("dotmat.gif");
}
@Override
protected void configureNewInstance(Instance instance) {
instance.addAttributeListener();
updatePorts(instance);
}
@Override
public Bounds getOffsetBounds(AttributeSet attrs) {
Object input = attrs.getValue(ATTR_INPUT_TYPE);
int cols = attrs.getValue(ATTR_MATRIX_COLS).intValue();
int rows = attrs.getValue(ATTR_MATRIX_ROWS).intValue();
if (input == INPUT_COLUMN) {
return Bounds.create(-5, -10 * rows, 10 * cols, 10 * rows);
} else if (input == INPUT_ROW) {
return Bounds.create(0, -5, 10 * cols, 10 * rows);
} else { // input == INPUT_SELECT
if (rows == 1) {
return Bounds.create(0, -5, 10 * cols, 10 * rows);
} else {
return Bounds.create(0, -5 * rows + 5, 10 * cols, 10 * rows);
}
}
}
private State getState(InstanceState state) {
int rows = state.getAttributeValue(ATTR_MATRIX_ROWS).intValue();
int cols = state.getAttributeValue(ATTR_MATRIX_COLS).intValue();
long clock = state.getTickCount();
State data = (State) state.getData();
if (data == null) {
data = new State(rows, cols, clock);
state.setData(data);
} else {
data.updateSize(rows, cols, clock);
}
return data;
}
@Override
protected void instanceAttributeChanged(Instance instance, Attribute<?> attr) {
if (attr == ATTR_MATRIX_ROWS || attr == ATTR_MATRIX_COLS
|| attr == ATTR_INPUT_TYPE) {
instance.recomputeBounds();
updatePorts(instance);
}
}
@Override
public void paintInstance(InstancePainter painter) {
Color onColor = painter.getAttributeValue(Io.ATTR_ON_COLOR);
Color offColor = painter.getAttributeValue(Io.ATTR_OFF_COLOR);
boolean drawSquare = painter.getAttributeValue(ATTR_DOT_SHAPE) == SHAPE_SQUARE;
State data = getState(painter);
long ticks = painter.getTickCount();
Bounds bds = painter.getBounds();
boolean showState = painter.getShowState();
Graphics g = painter.getGraphics();
int rows = data.rows;
int cols = data.cols;
for (int j = 0; j < rows; j++) {
for (int i = 0; i < cols; i++) {
int x = bds.getX() + 10 * i;
int y = bds.getY() + 10 * j;
if (showState) {
Value val = data.get(j, i, ticks);
Color c;
if (val == Value.TRUE)
c = onColor;
else if (val == Value.FALSE)
c = offColor;
else
c = Value.ERROR_COLOR;
g.setColor(c);
if (drawSquare)
g.fillRect(x, y, 10, 10);
else
g.fillOval(x + 1, y + 1, 8, 8);
} else {
g.setColor(Color.GRAY);
g.fillOval(x + 1, y + 1, 8, 8);
}
}
}
g.setColor(Color.BLACK);
GraphicsUtil.switchToWidth(g, 2);
g.drawRect(bds.getX(), bds.getY(), bds.getWidth(), bds.getHeight());
GraphicsUtil.switchToWidth(g, 1);
painter.drawPorts();
}
@Override
public void propagate(InstanceState state) {
Object type = state.getAttributeValue(ATTR_INPUT_TYPE);
int rows = state.getAttributeValue(ATTR_MATRIX_ROWS).intValue();
int cols = state.getAttributeValue(ATTR_MATRIX_COLS).intValue();
long clock = state.getTickCount();
long persist = clock + state.getAttributeValue(ATTR_PERSIST).intValue();
State data = getState(state);
if (type == INPUT_ROW) {
for (int i = 0; i < rows; i++) {
data.setRow(i, state.getPortValue(i), persist);
}
} else if (type == INPUT_COLUMN) {
for (int i = 0; i < cols; i++) {
data.setColumn(i, state.getPortValue(i), persist);
}
} else if (type == INPUT_SELECT) {
data.setSelect(state.getPortValue(1), state.getPortValue(0),
persist);
} else {
throw new RuntimeException("unexpected matrix type");
}
}
private void updatePorts(Instance instance) {
Object input = instance.getAttributeValue(ATTR_INPUT_TYPE);
int rows = instance.getAttributeValue(ATTR_MATRIX_ROWS).intValue();
int cols = instance.getAttributeValue(ATTR_MATRIX_COLS).intValue();
Port[] ps;
if (input == INPUT_COLUMN) {
ps = new Port[cols];
for (int i = 0; i < cols; i++) {
ps[i] = new Port(10 * i, 0, Port.INPUT, rows);
}
} else if (input == INPUT_ROW) {
ps = new Port[rows];
for (int i = 0; i < rows; i++) {
ps[i] = new Port(0, 10 * i, Port.INPUT, cols);
}
} else {
if (rows <= 1) {
ps = new Port[] { new Port(0, 0, Port.INPUT, cols) };
} else if (cols <= 1) {
ps = new Port[] { new Port(0, 0, Port.INPUT, rows) };
} else {
ps = new Port[] { new Port(0, 0, Port.INPUT, cols),
new Port(0, 10, Port.INPUT, rows) };
}
}
instance.setPorts(ps);
}
}