/******************************************************************************* * 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.awt.event.MouseEvent; import com.cburch.logisim.data.Attribute; import com.cburch.logisim.data.Attributes; import com.cburch.logisim.data.BitWidth; import com.cburch.logisim.data.Bounds; import com.cburch.logisim.data.Location; import com.cburch.logisim.data.Value; import com.cburch.logisim.instance.InstanceData; import com.cburch.logisim.instance.InstanceFactory; import com.cburch.logisim.instance.InstancePainter; import com.cburch.logisim.instance.InstancePoker; import com.cburch.logisim.instance.InstanceState; import com.cburch.logisim.instance.Port; import com.cburch.logisim.tools.key.BitWidthConfigurator; import com.cburch.logisim.util.GraphicsUtil; public class Joystick extends InstanceFactory { public static class Poker extends InstancePoker { @Override public void mouseDragged(InstanceState state, MouseEvent e) { Location loc = state.getInstance().getLocation(); int cx = loc.getX() - 15; int cy = loc.getY() + 5; updateState(state, e.getX() - cx, e.getY() - cy); } @Override public void mousePressed(InstanceState state, MouseEvent e) { mouseDragged(state, e); } @Override public void mouseReleased(InstanceState state, MouseEvent e) { updateState(state, 0, 0); } @Override public void paint(InstancePainter painter) { State state = (State) painter.getData(); if (state == null) { state = new State(0, 0); painter.setData(state); } Location loc = painter.getLocation(); int x = loc.getX(); int y = loc.getY(); Graphics g = painter.getGraphics(); g.setColor(Color.WHITE); g.fillRect(x - 20, y, 10, 10); GraphicsUtil.switchToWidth(g, 3); g.setColor(Color.BLACK); int dx = state.xPos; int dy = state.yPos; int x0 = x - 15 + (dx > 5 ? 1 : dx < -5 ? -1 : 0); int y0 = y + 5 + (dy > 5 ? 1 : dy < 0 ? -1 : 0); int x1 = x - 15 + dx; int y1 = y + 5 + dy; g.drawLine(x0, y0, x1, y1); Color ballColor = painter.getAttributeValue(Io.ATTR_COLOR); Joystick.drawBall(g, x1, y1, ballColor, true); } private void updateState(InstanceState state, int dx, int dy) { State s = (State) state.getData(); if (dx < -14) dx = -14; if (dy < -14) dy = -14; if (dx > 14) dx = 14; if (dy > 14) dy = 14; if (s == null) { s = new State(dx, dy); state.setData(s); } else { s.xPos = dx; s.yPos = dy; } state.getInstance().fireInvalidated(); } } private static class State implements InstanceData, Cloneable { private int xPos; private int yPos; public State(int x, int y) { xPos = x; yPos = y; } @Override public Object clone() { try { return super.clone(); } catch (CloneNotSupportedException e) { return null; } } } private static void drawBall(Graphics g, int x, int y, Color c, boolean inColor) { if (inColor) { g.setColor(c == null ? Color.RED : c); } else { int hue = c == null ? 128 : (c.getRed() + c.getGreen() + c .getBlue()) / 3; g.setColor(new Color(hue, hue, hue)); } GraphicsUtil.switchToWidth(g, 1); g.fillOval(x - 4, y - 4, 8, 8); g.setColor(Color.BLACK); g.drawOval(x - 4, y - 4, 8, 8); } static final Attribute<BitWidth> ATTR_WIDTH = Attributes.forBitWidth( "bits", Strings.getter("ioBitWidthAttr"), 2, 5); public Joystick() { super("Joystick", Strings.getter("joystickComponent")); setAttributes(new Attribute[] { ATTR_WIDTH, Io.ATTR_COLOR }, new Object[] { BitWidth.create(4), Color.RED }); setKeyConfigurator(new BitWidthConfigurator(ATTR_WIDTH, 2, 5)); setOffsetBounds(Bounds.create(-30, -10, 30, 30)); setIconName("joystick.gif"); setPorts(new Port[] { new Port(0, 0, Port.OUTPUT, ATTR_WIDTH), new Port(0, 10, Port.OUTPUT, ATTR_WIDTH), }); setInstancePoker(Poker.class); } @Override public void paintGhost(InstancePainter painter) { Graphics g = painter.getGraphics(); GraphicsUtil.switchToWidth(g, 2); g.drawRoundRect(-30, -10, 30, 30, 8, 8); } @Override public void paintInstance(InstancePainter painter) { Location loc = painter.getLocation(); int x = loc.getX(); int y = loc.getY(); Graphics g = painter.getGraphics(); g.drawRoundRect(x - 30, y - 10, 30, 30, 8, 8); g.drawRoundRect(x - 28, y - 8, 26, 26, 4, 4); drawBall(g, x - 15, y + 5, painter.getAttributeValue(Io.ATTR_COLOR), painter.shouldDrawColor()); painter.drawPorts(); } @Override public void propagate(InstanceState state) { BitWidth bits = state.getAttributeValue(ATTR_WIDTH); int dx; int dy; State s = (State) state.getData(); if (s == null) { dx = 0; dy = 0; } else { dx = s.xPos; dy = s.yPos; } int steps = (1 << bits.getWidth()) - 1; dx = (dx + 14) * steps / 29 + 1; dy = (dy + 14) * steps / 29 + 1; if (bits.getWidth() > 4) { if (dx >= steps / 2) dx++; if (dy >= steps / 2) dy++; } state.setPort(0, Value.createKnown(bits, dx), 1); state.setPort(1, Value.createKnown(bits, dy), 1); } }