/* Copyright (c) 2010, Carl Burch. License information is located in the
* com.cburch.logisim.Main source code and at www.cburch.com/logisim/. */
package com.cburch.logisim.std.gates;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.GeneralPath;
import java.awt.geom.Point2D;
import java.util.HashMap;
import com.cburch.logisim.data.Direction;
import com.cburch.logisim.data.Location;
import com.cburch.logisim.data.Value;
import com.cburch.logisim.instance.InstancePainter;
import com.cburch.logisim.util.GraphicsUtil;
/**
* PainterShaped actually renders gates onto the screen.
*
*/
public class PainterShaped {
private static final GeneralPath PATH_NARROW;
private static final GeneralPath PATH_MEDIUM;
private static final GeneralPath PATH_WIDE;
private static final GeneralPath SHIELD_NARROW;
private static final GeneralPath SHIELD_MEDIUM;
private static final GeneralPath SHIELD_WIDE;
static {
PATH_NARROW = new GeneralPath();
PATH_NARROW.moveTo(0, 0);
PATH_NARROW.quadTo(-10, -15, -30, -15);
PATH_NARROW.quadTo(-22, 0, -30, 15);
PATH_NARROW.quadTo(-10, 15, 0, 0);
PATH_NARROW.closePath();
PATH_MEDIUM = new GeneralPath();
PATH_MEDIUM.moveTo(0, 0);
PATH_MEDIUM.quadTo(-20, -25, -50, -25);
PATH_MEDIUM.quadTo(-37, 0, -50, 25);
PATH_MEDIUM.quadTo(-20, 25, 0, 0);
PATH_MEDIUM.closePath();
PATH_WIDE = new GeneralPath();
PATH_WIDE.moveTo(0, 0);
PATH_WIDE.quadTo(-25, -35, -70, -35);
PATH_WIDE.quadTo(-50, 0, -70, 35);
PATH_WIDE.quadTo(-25, 35, 0, 0);
PATH_WIDE.closePath();
SHIELD_NARROW = new GeneralPath();
SHIELD_NARROW.moveTo(-30, -15);
SHIELD_NARROW.quadTo(-22, 0, -30, 15);
SHIELD_MEDIUM = new GeneralPath();
SHIELD_MEDIUM.moveTo(-50, -25);
SHIELD_MEDIUM.quadTo(-37, 0, -50, 25);
SHIELD_WIDE = new GeneralPath();
SHIELD_WIDE.moveTo(-70, -35);
SHIELD_WIDE.quadTo(-50, 0, -70, 35);
}
private PainterShaped() { }
private static HashMap<Integer,int[]> INPUT_LENGTHS = new HashMap<Integer,int[]>();
static void paintAnd(InstancePainter painter, int width, int height) {
Graphics g = painter.getGraphics();
GraphicsUtil.switchToWidth(g, 2);
int[] xp = new int[] { -width / 2, -width + 1, -width + 1, -width / 2 };
int[] yp = new int[] { -width / 2, -width / 2, width / 2, width / 2 };
GraphicsUtil.drawCenteredArc(g, -width / 2, 0, width / 2, -90, 180);
g.drawPolyline(xp, yp, 4);
if (height > width) {
g.drawLine(-width + 1, -height / 2, -width + 1, height / 2);
}
}
static void paintOr(InstancePainter painter, int width, int height) {
Graphics g = painter.getGraphics();
GraphicsUtil.switchToWidth(g, 2);
GeneralPath path;
if (width < 40) {
path = PATH_NARROW;
} else if (width < 60) {
path = PATH_MEDIUM;
} else {
path = PATH_WIDE;
}
((Graphics2D) g).draw(path);
if (height > width) {
paintShield(g, 0, width, height);
}
}
static void paintNot(InstancePainter painter) {
Graphics g = painter.getGraphics();
GraphicsUtil.switchToWidth(g, 2);
if (painter.getAttributeValue(NotGate.ATTR_SIZE) == NotGate.SIZE_NARROW) {
GraphicsUtil.switchToWidth(g, 2);
int[] xp = new int[4];
int[] yp = new int[4];
xp[0] = -6; yp[0] = 0;
xp[1] = -19; yp[1] = -6;
xp[2] = -19; yp[2] = 6;
xp[3] = -6; yp[3] = 0;
g.drawPolyline(xp, yp, 4);
g.drawOval(-6, -3, 6, 6);
} else {
int[] xp = new int[4];
int[] yp = new int[4];
xp[0] = -10; yp[0] = 0;
xp[1] = -29; yp[1] = -7;
xp[2] = -29; yp[2] = 7;
xp[3] = -10; yp[3] = 0;
g.drawPolyline(xp, yp, 4);
g.drawOval(-9, -4, 9, 9);
}
}
static void paintXor(InstancePainter painter, int width, int height) {
Graphics g = painter.getGraphics();
paintOr(painter, width - 10, width - 10);
paintShield(g, -10, width - 10, height);
}
private static void paintShield(Graphics g, int xlate,
int width, int height) {
GraphicsUtil.switchToWidth(g, 2);
g.translate(xlate, 0);
((Graphics2D) g).draw(computeShield(width, height));
g.translate(-xlate, 0);
}
private static GeneralPath computeShield(int width, int height) {
GeneralPath base;
if (width < 40) {
base = SHIELD_NARROW;
} else if (width < 60) {
base = SHIELD_MEDIUM;
} else {
base = SHIELD_WIDE;
}
// no wings
if (height <= width) {
return base;
// we need to add wings
} else {
int wingHeight = (height - width) / 2;
int dx = Math.min(20, wingHeight / 4);
GeneralPath path = new GeneralPath();
path.moveTo(-width, -height / 2);
path.quadTo(-width + dx, -(width + height) / 4, -width, -width / 2);
path.append(base, true);
path.quadTo(-width + dx, (width + height) / 4, -width, height / 2);
return path;
}
}
static void paintInputLines(InstancePainter painter, AbstractGate factory) {
Location loc = painter.getLocation();
boolean printView = painter.isPrintView();
GateAttributes attrs = (GateAttributes) painter.getAttributeSet();
Direction facing = attrs.facing;
int inputs = attrs.inputs;
int negated = attrs.negated;
int[] lengths = getInputLineLengths(attrs, factory);
// drawing ghost - negation bubbles only
if (painter.getInstance() == null) {
for (int i = 0; i < inputs; i++) {
boolean iNegated = ((negated >> i) & 1) == 1;
if (iNegated) {
Location offs = factory.getInputOffset(attrs, i);
Location loci = loc.translate(offs.getX(), offs.getY());
Location cent = loci.translate(facing, lengths[i] + 5);
painter.drawDongle(cent.getX(), cent.getY());
}
}
} else {
Graphics g = painter.getGraphics();
Color baseColor = g.getColor();
GraphicsUtil.switchToWidth(g, 3);
for (int i = 0; i < inputs; i++) {
Location offs = factory.getInputOffset(attrs, i);
Location src = loc.translate(offs.getX(), offs.getY());
int len = lengths[i];
if (len != 0 && (!printView || painter.isPortConnected(i + 1))) {
if (painter.getShowState()) {
Value val = painter.getPort(i + 1);
g.setColor(val.getColor());
} else {
g.setColor(baseColor);
}
Location dst = src.translate(facing, len);
g.drawLine(src.getX(), src.getY(), dst.getX(), dst.getY());
}
if (((negated >> i) & 1) == 1) {
Location cent = src.translate(facing, lengths[i] + 5);
g.setColor(baseColor);
painter.drawDongle(cent.getX(), cent.getY());
GraphicsUtil.switchToWidth(g, 3);
}
}
}
}
private static int[] getInputLineLengths(GateAttributes attrs, AbstractGate factory) {
int inputs = attrs.inputs;
int mainHeight = ((Integer) attrs.size.getValue()).intValue();
Integer key = Integer.valueOf(inputs * 31 + mainHeight);
Object ret = INPUT_LENGTHS.get(key);
if (ret != null) {
return (int[]) ret;
}
Direction facing = attrs.facing;
if (facing != Direction.EAST) {
attrs = (GateAttributes) attrs.clone();
attrs.facing = Direction.EAST;
}
int[] lengths = new int[inputs];
INPUT_LENGTHS.put(key, lengths);
int width = mainHeight;
Location loc0 = OrGate.FACTORY.getInputOffset(attrs, 0);
Location locn = OrGate.FACTORY.getInputOffset(attrs, inputs - 1);
int totalHeight = 10 + loc0.manhattanDistanceTo(locn);
if (totalHeight < width) {
totalHeight = width;
}
GeneralPath path = computeShield(width, totalHeight);
for (int i = 0; i < inputs; i++) {
Location loci = OrGate.FACTORY.getInputOffset(attrs, i);
Point2D p = new Point2D.Float(loci.getX() + 1, loci.getY());
int iters = 0;
while (path.contains(p) && iters < 15) {
iters++;
p.setLocation(p.getX() + 1, p.getY());
}
if (iters >= 15) {
iters = 0;
}
lengths[i] = iters;
}
return lengths;
}
}