/*******************************************************************************
* 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.circuit.appear;
import java.awt.Color;
import java.awt.Graphics;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.cburch.draw.shapes.Curve;
import com.cburch.draw.model.CanvasObject;
import com.cburch.draw.shapes.DrawAttr;
import com.cburch.draw.shapes.Rectangle;
import com.cburch.draw.shapes.Text;
import com.cburch.draw.util.EditableLabel;
import com.cburch.logisim.circuit.Wire;
import com.cburch.logisim.data.Direction;
import com.cburch.logisim.data.Location;
import com.cburch.logisim.instance.Instance;
import com.cburch.logisim.instance.StdAttr;
import com.cburch.logisim.std.wiring.Pin;
class DefaultAppearance {
private static class CompareLocations implements Comparator<Instance> {
private boolean byX;
CompareLocations(boolean byX) {
this.byX = byX;
}
public int compare(Instance a, Instance b) {
Location aloc = a.getLocation();
Location bloc = b.getLocation();
if (byX) {
int ax = aloc.getX();
int bx = bloc.getX();
if (ax != bx) {
return ax < bx ? -1 : 1;
}
} else {
int ay = aloc.getY();
int by = bloc.getY();
if (ay != by) {
return ay < by ? -1 : 1;
}
}
return aloc.compareTo(bloc);
}
}
public static List<CanvasObject> build(Collection<Instance> pins,
boolean NamedBox,
String CircuitName,
Graphics g) {
if (NamedBox) {
return new_build(pins,CircuitName,g);
} else {
return old_build(pins);
}
}
private static List<CanvasObject> old_build(Collection<Instance> pins) {
Map<Direction,List<Instance>> edge;
edge = new HashMap<Direction,List<Instance>>();
edge.put(Direction.NORTH, new ArrayList<Instance>());
edge.put(Direction.SOUTH, new ArrayList<Instance>());
edge.put(Direction.EAST, new ArrayList<Instance>());
edge.put(Direction.WEST, new ArrayList<Instance>());
for (Instance pin : pins) {
Direction pinFacing = pin.getAttributeValue(StdAttr.FACING);
Direction pinEdge = pinFacing.reverse();
List<Instance> e = edge.get(pinEdge);
e.add(pin);
}
for (Map.Entry<Direction, List<Instance>> entry : edge.entrySet()) {
sortPinList(entry.getValue(), entry.getKey());
}
int numNorth = edge.get(Direction.NORTH).size();
int numSouth = edge.get(Direction.SOUTH).size();
int numEast = edge.get(Direction.EAST).size();
int numWest = edge.get(Direction.WEST).size();
int maxVert = Math.max(numNorth, numSouth);
int maxHorz = Math.max(numEast, numWest);
int offsNorth = computeOffset(numNorth, numSouth, maxHorz);
int offsSouth = computeOffset(numSouth, numNorth, maxHorz);
int offsEast = computeOffset(numEast, numWest, maxVert);
int offsWest = computeOffset(numWest, numEast, maxVert);
int width = computeDimension(maxVert, maxHorz);
int height = computeDimension(maxHorz, maxVert);
// compute position of anchor relative to top left corner of box
int ax;
int ay;
if (numEast > 0) { // anchor is on east side
ax = width;
ay = offsEast;
} else if (numNorth > 0) { // anchor is on north side
ax = offsNorth;
ay = 0;
} else if (numWest > 0) { // anchor is on west side
ax = 0;
ay = offsWest;
} else if (numSouth > 0) { // anchor is on south side
ax = offsSouth;
ay = height;
} else { // anchor is top left corner
ax = 0;
ay = 0;
}
// place rectangle so anchor is on the grid
int rx = OFFS + (9 - (ax + 9) % 10);
int ry = OFFS + (9 - (ay + 9) % 10);
Location e0 = Location.create(rx + (width - 8) / 2, ry + 1);
Location e1 = Location.create(rx + (width + 8) / 2, ry + 1);
Location ct = Location.create(rx + width / 2, ry + 11);
Curve notch = new Curve(e0, e1, ct);
notch.setValue(DrawAttr.STROKE_WIDTH, Integer.valueOf(2));
notch.setValue(DrawAttr.STROKE_COLOR, Color.GRAY);
Rectangle rect = new Rectangle(rx, ry, width, height);
rect.setValue(DrawAttr.STROKE_WIDTH, Integer.valueOf(2));
List<CanvasObject> ret = new ArrayList<CanvasObject>();
ret.add(notch);
ret.add(rect);
placePins(ret, edge.get(Direction.WEST),
rx, ry + offsWest, 0, 10);
placePins(ret, edge.get(Direction.EAST),
rx + width, ry + offsEast, 0, 10);
placePins(ret, edge.get(Direction.NORTH),
rx + offsNorth, ry, 10, 0);
placePins(ret, edge.get(Direction.SOUTH),
rx + offsSouth, ry + height, 10, 0);
ret.add(new AppearanceAnchor(Location.create(rx + ax, ry + ay)));
return ret;
}
private static List<CanvasObject> new_build(Collection<Instance> pins, String CircuitName, Graphics g) {
Map<Direction, List<Instance>> edge;
edge = new HashMap<Direction, List<Instance>>();
edge.put(Direction.EAST, new ArrayList<Instance>());
edge.put(Direction.WEST, new ArrayList<Instance>());
int MaxLeftLabelLength = 0;
int MaxRightLabelLength = 0;
int TitleWidth = CircuitName.length()*DrawAttr.FixedFontCharWidth;
if (!pins.isEmpty()) {
for (Instance pin : pins) {
Direction pinEdge;
Text label = new Text(0,0,pin.getAttributeValue(StdAttr.LABEL));
int LabelWidth = label.getText().length()*DrawAttr.FixedFontCharWidth;
if (pin.getAttributeValue(Pin.ATTR_TYPE)) {
pinEdge=Direction.EAST;
if (LabelWidth>MaxRightLabelLength)
MaxRightLabelLength = LabelWidth;
}
else {
pinEdge=Direction.WEST;
if (LabelWidth>MaxLeftLabelLength)
MaxLeftLabelLength = LabelWidth;
}
List<Instance> e = edge.get(pinEdge);
e.add(pin);
}
}
for (Map.Entry<Direction, List<Instance>> entry : edge.entrySet()) {
sortPinList(entry.getValue(), entry.getKey());
}
int numEast = edge.get(Direction.EAST).size();
int numWest = edge.get(Direction.WEST).size();
int maxVert = Math.max(numEast, numWest);
int dy = ((DrawAttr.FixedFontHeight+(DrawAttr.FixedFontHeight>>2)+5)/10)*10;
int textWidth = (MaxLeftLabelLength+MaxRightLabelLength+35) < (TitleWidth+15) ? TitleWidth+15 :
(MaxLeftLabelLength+MaxRightLabelLength+35);
int Thight = ((DrawAttr.FixedFontHeight+10)/10)*10;
int width = (textWidth/10)*10+20;
int height = (maxVert > 0) ? maxVert*dy+Thight : 10+Thight;
int sdy = (DrawAttr.FixedFontAscent-DrawAttr.FixedFontDescent)>>1;
// compute position of anchor relative to top left corner of box
int ax;
int ay;
if (numEast > 0) { // anchor is on east side
ax = width;
ay = 10;
} else if (numWest > 0) { // anchor is on west side
ax = 0;
ay = 10;
} else { // anchor is top left corner
ax = 0;
ay = 0;
}
// place rectangle so anchor is on the grid
int rx = OFFS + (9 - (ax + 9) % 10);
int ry = OFFS + (9 - (ay + 9) % 10);
List<CanvasObject> ret = new ArrayList<CanvasObject>();
placePins(ret, edge.get(Direction.WEST), rx, ry + 10, 0, dy,true,
sdy);
placePins(ret, edge.get(Direction.EAST), rx + width, ry + 10, 0,
dy,false,sdy);
Rectangle rect = new Rectangle(rx+10,ry+height-Thight,width-20,Thight);
rect.setValue(DrawAttr.STROKE_WIDTH, Integer.valueOf(1));
rect.setValue(DrawAttr.PAINT_TYPE, DrawAttr.PAINT_FILL);
rect.setValue(DrawAttr.FILL_COLOR, Color.black);
ret.add(rect);
rect = new Rectangle(rx+10, ry, width-20, height);
rect.setValue(DrawAttr.STROKE_WIDTH, Integer.valueOf(2));
ret.add(rect);
Text label = new Text(rx+(width>>1),ry+(height-DrawAttr.FixedFontDescent-5),CircuitName);
label.getLabel().setHorizontalAlignment(EditableLabel.CENTER);
label.getLabel().setColor(Color.white);
label.getLabel().setFont(DrawAttr.DEFAULT_NAME_FONT);
ret.add(label);
ret.add(new AppearanceAnchor(Location.create(rx + ax, ry + ay)));
return ret;
}
private static int computeDimension(int maxThis, int maxOthers) {
if (maxThis < 3) {
return 30;
} else if (maxOthers == 0) {
return 10 * maxThis;
} else {
return 10 * maxThis + 10;
}
}
private static int computeOffset(int numFacing, int numOpposite, int maxOthers) {
int maxThis = Math.max(numFacing, numOpposite);
int maxOffs;
switch (maxThis) {
case 0:
case 1:
maxOffs = (maxOthers == 0 ? 15 : 10);
break;
case 2:
maxOffs = 10;
break;
default:
maxOffs = (maxOthers == 0 ? 5 : 10);
}
return maxOffs + 10 * ((maxThis - numFacing) / 2);
}
private static void placePins(List<CanvasObject> dest, List<Instance> pins,
int x, int y, int dx, int dy) {
for (Instance pin : pins) {
dest.add(new AppearancePort(Location.create(x, y), pin));
x += dx;
y += dy;
}
}
private static void placePins(List<CanvasObject> dest, List<Instance> pins,
int x, int y, int dx, int dy, boolean LeftSide, int ldy) {
int halign;
Color color = Color.DARK_GRAY;
int ldx;
for (Instance pin : pins) {
int offset = (pin.getAttributeValue(StdAttr.WIDTH).getWidth() > 1) ? Wire.WIDTH_BUS>>1:Wire.WIDTH>>1;
int height = (pin.getAttributeValue(StdAttr.WIDTH).getWidth() > 1) ? Wire.WIDTH_BUS:Wire.WIDTH;
Rectangle rect;
if (LeftSide) {
ldx=15;
halign=EditableLabel.LEFT;
rect = new Rectangle(x,y-offset,10,height);
} else {
ldx=-15;
halign=EditableLabel.RIGHT;
rect = new Rectangle(x-10,y-offset,10,height);
}
rect.setValue(DrawAttr.STROKE_WIDTH, Integer.valueOf(1));
rect.setValue(DrawAttr.PAINT_TYPE, DrawAttr.PAINT_FILL);
rect.setValue(DrawAttr.FILL_COLOR, Color.BLACK);
dest.add(rect);
dest.add(new AppearancePort(Location.create(x, y), pin));
if (pin.getAttributeSet().containsAttribute(StdAttr.LABEL)) {
Text label = new Text(x+ldx,y+ldy,pin.getAttributeValue(StdAttr.LABEL));
label.getLabel().setHorizontalAlignment(halign);
label.getLabel().setColor(color);
label.getLabel().setFont(DrawAttr.DEFAULT_FIXED_PICH_FONT);
dest.add(label);
}
x += dx;
y += dy;
}
}
static void sortPinList(List<Instance> pins, Direction facing) {
if (facing == Direction.NORTH || facing == Direction.SOUTH) {
Comparator<Instance> sortHorizontal = new CompareLocations(true);
Collections.sort(pins, sortHorizontal);
} else {
Comparator<Instance> sortVertical = new CompareLocations(false);
Collections.sort(pins, sortVertical);
}
}
private static final int OFFS = 50;
private DefaultAppearance() {
}
}