/* * funCKit - functional Circuit Kit * Copyright (C) 2013 Lukas Elsner <open@mindrunner.de> * Copyright (C) 2013 Peter Dahlberg <catdog2@tuxzone.org> * Copyright (C) 2013 Julian Stier <mail@julian-stier.de> * Copyright (C) 2013 Sebastian Vetter <mail@b4sti.eu> * Copyright (C) 2013 Thomas Poxrucker <poxrucker_t@web.de> * Copyright (C) 2013 Alexander Treml <alex.treml@directbox.com> * * This program 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. * * This program 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 this program. If not, see <http://www.gnu.org/licenses/>. */ package de.sep2011.funckit.util; import static java.lang.Math.max; import de.sep2011.funckit.model.graphmodel.AccessPoint; import de.sep2011.funckit.model.graphmodel.Brick; import de.sep2011.funckit.model.graphmodel.Circuit; import de.sep2011.funckit.model.graphmodel.Component; import de.sep2011.funckit.model.graphmodel.ComponentType; import de.sep2011.funckit.model.graphmodel.Element; import de.sep2011.funckit.model.graphmodel.ElementDispatcher; import de.sep2011.funckit.model.graphmodel.Input; import de.sep2011.funckit.model.graphmodel.Output; import de.sep2011.funckit.model.graphmodel.Switch; import de.sep2011.funckit.model.graphmodel.Wire; import de.sep2011.funckit.model.graphmodel.implementations.And; import de.sep2011.funckit.model.graphmodel.implementations.ComponentTypeImpl; import de.sep2011.funckit.model.graphmodel.implementations.IdPoint; import de.sep2011.funckit.model.graphmodel.implementations.Light; import de.sep2011.funckit.model.graphmodel.implementations.Not; import de.sep2011.funckit.model.graphmodel.implementations.Or; import de.sep2011.funckit.model.graphmodel.implementations.SwitchImpl; import de.sep2011.funckit.model.graphmodel.implementations.WireImpl; import de.sep2011.funckit.model.graphmodel.implementations.commands.ConnectCommand; import de.sep2011.funckit.util.command.Command; import de.sep2011.funckit.util.command.CommandDispatcher; import de.sep2011.funckit.util.command.SimpleCommandCombiner; import java.awt.Point; import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; /** * Utility class for some logic belonging to the graphmodel, but which is better * not integrated directly in the graphmodel itself. */ public class GraphmodelUtil { /** * Converts the given {@link Circuit} to an {@link ComponentType} by * converting all {@link Switch}es and {@link Light}s in the {@link Circuit} * to {@link IdPoint}s and their {@link Input}/{@link Output} gets the * {@link ComponentType} {@link Input}/{@link Output} with the name of the * {@link Switch}/{@link Light}. This can be reverted with . * * @param circuit * the {@link Circuit} to convert. * @param typeName * the name of the generated {@link ComponentType}. * @param copyCircuit * @return a ComponentType */ public static ComponentType convertToComponentType(Circuit circuit, String typeName, boolean copyCircuit) { if (copyCircuit) { circuit = circuit.getCopy(); } Set<Input> inputs = new LinkedHashSet<Input>(); Set<Output> outputs = new LinkedHashSet<Output>(); Map<AccessPoint, String> names = new LinkedHashMap<AccessPoint, String>(); Set<Element> elements = new LinkedHashSet<Element>(circuit.getElements()); List<Element> switches = new LinkedList<Element>(); List<Element> lights = new LinkedList<Element>(); for (Element e : elements) { if (e instanceof SwitchImpl) { switches.add(e); } else if (e instanceof Light) { lights.add(e); } } Collections.sort(switches, new VerticalThanHorizontalOrderComparator()); Collections.sort(lights, new VerticalThanHorizontalOrderComparator()); for (Element e : switches) { SwitchImpl sw = (SwitchImpl) e; IdPoint in = new IdPoint(sw.getPosition()); in.setName(sw.getName()); in.setOrientation(in.getOrientation()); for (Wire w : sw.getOutputO().getWires()) { if (w.getFirstAccessPoint() == sw.getOutputO()) { w.setFirstAccessPoint(in.getOutputO()); } else { w.setSecondAccessPoint(in.getOutputO()); } in.getOutputO().addWire(w); } circuit.removeBrick(sw); circuit.addBrick(in); inputs.add(in.getInputA()); names.put(in.getInputA(), sw.getName()); } for (Element e : lights) { Light li = (Light) e; IdPoint out = new IdPoint(li.getPosition()); out.setName(li.getName()); out.setOrientation(li.getOrientation()); for (Wire w : li.getInputA().getWires()) { if (w.getFirstAccessPoint() == li.getInputA()) { w.setFirstAccessPoint(out.getInputA()); } else { w.setSecondAccessPoint(out.getInputA()); } out.getInputA().addWire(w); } circuit.removeBrick(li); circuit.addBrick(out); outputs.add(out.getOutputO()); names.put(out.getOutputO(), li.getName()); } ComponentType type = new ComponentTypeImpl(circuit, typeName, inputs, outputs); for (Entry<AccessPoint, String> entry : names.entrySet()) { type.setName(entry.getKey(), entry.getValue()); } return type; } /** * Reverts a {@link ComponentType} that was generated by * {@link #convertToComponentType(Circuit, String, boolean)} to the original * circuit. * * @param type * the {@link ComponentType} that was generated by * {@link #convertToComponentType(Circuit, String, boolean)}. * @param copyCircuit * copy the circuit of the given {@link ComponentType}? * @return the reverted {@link Circuit}. */ public static Circuit revertToCircuit(ComponentType type, boolean copyCircuit) { Circuit circuit = type.getCircuit(); Map<Brick, Brick> oldToNewBrickMap = null; if (copyCircuit) { Pair<Map<Brick, Brick>, Circuit> pair = circuit.getBrickMapAndCopy(); circuit = pair.getRight(); oldToNewBrickMap = pair.getLeft(); } for (Input in : type.getInputs()) { Brick brick = in.getBrick(); if (copyCircuit) { assert oldToNewBrickMap != null; brick = oldToNewBrickMap.get(brick); } if (brick instanceof IdPoint) { IdPoint point = (IdPoint) brick; SwitchImpl sw = new SwitchImpl(point.getPosition()); sw.setName(point.getName()); sw.setOrientation(point.getOrientation()); for (Wire w : point.getOutputO().getWires()) { if (w.getFirstAccessPoint() == point.getOutputO()) { w.setFirstAccessPoint(sw.getOutputO()); } else { w.setSecondAccessPoint(sw.getOutputO()); } sw.getOutputO().addWire(w); } circuit.removeBrick(point); assert !circuit.getElements().contains(point); circuit.addBrick(sw); } } for (Output out : type.getOutputs()) { Brick brick = out.getBrick(); if (copyCircuit) { assert oldToNewBrickMap != null; brick = oldToNewBrickMap.get(brick); } if (brick instanceof IdPoint) { IdPoint point = (IdPoint) brick; Light li = new Light(point.getPosition()); li.setName(point.getName()); li.setOrientation(point.getOrientation()); for (Wire w : point.getInputA().getWires()) { if (w.getFirstAccessPoint() == point.getInputA()) { w.setFirstAccessPoint(li.getInputA()); } else { w.setSecondAccessPoint(li.getInputA()); } li.getInputA().addWire(w); } circuit.removeBrick(point); assert !circuit.getElements().contains(point); circuit.addBrick(li); } } return circuit; } /** * Comparator which compares the y-Coordinate. When they are equal it * compares the x-Coordinate. */ public static class VerticalThanHorizontalOrderComparator implements Comparator<Element>, Serializable { private static final long serialVersionUID = 4784080254850188924L; @Override public int compare(Element o1, Element o2) { int vertical = o1.getPosition().y - o2.getPosition().y; return vertical == 0 ? o1.getPosition().x - o2.getPosition().x : vertical; } } /** * Comparator which compares the y-Coordinate. When they are equal it * compares the x-Coordinate. */ private static class VerticalThanHorizontalOrderComparatorForAccessPoints implements Comparator<AccessPoint>, Serializable { private static final long serialVersionUID = 4784080254850188924L; @Override public int compare(AccessPoint o1, AccessPoint o2) { int vertical = o1.getPosition().y - o2.getPosition().y; return vertical == 0 ? o1.getPosition().x - o2.getPosition().x : vertical; } } /** * Connects the {@link Output}s of the first {@link Brick} with the {@link Input}s of the second {@link Brick}. * This is done pairwise, starting with the topmost {@link AccessPoint}s. If one {@link Brick} has more * {@link AccessPoint}s than the other the remain ones are not connected. * @param circuit the circuit the {@link Brick}s are an. * @param dispatcher the {@link CommandDispatcher} to use for connecting. * @param firstBrick the first {@link Brick} to connect. Here the {@link Output}s get connected. * @param secondBrick the second {@link Brick} to connect. Here the {@link Input}s get connected. * @param onlyEmpty only connect unconnected {@link AccessPoint}s? * @param offset the offset to use for the {@link Brick} with less {@link AccessPoint}s. If the offset is too big * the maximum that can be used without reducing the amount of connected {@link Wire}s is used. * @return {@link Pair} of the actually used offset and the created {@link Wire}s. */ public static void connectBricks(Circuit circuit, CommandDispatcher dispatcher, Brick firstBrick, Brick secondBrick, boolean onlyEmpty, int offset) { int[] intResults = new int[3]; Pair<List<AccessPoint>, List<AccessPoint>> listPair = connectBricksHelper(firstBrick, secondBrick, onlyEmpty, offset, intResults); List<Command> commands = new ArrayList<Command>(intResults[0]); for(int i = 0; i < intResults[0]; i++) { Command connectCommand = new ConnectCommand(circuit, listPair.getLeft().get(i+intResults[1]), listPair.getRight().get(i+intResults[2])); commands.add(connectCommand); } if (commands.size() > 0) { dispatcher.dispatch(new SimpleCommandCombiner(commands)); } } /** * Connects the {@link Output}s of the first {@link Brick} with the {@link Input}s of the second {@link Brick}. * This is done pairwise, starting with the topmost {@link AccessPoint}s. If one {@link Brick} has more * {@link AccessPoint}s than the other the remain ones are not connected. * This method only creates the needed {@link Wire}s * @param firstBrick the first {@link Brick} to connect. Here the {@link Output}s get connected. * @param secondBrick the second {@link Brick} to connect. Here the {@link Input}s get connected. * @param onlyEmpty only connect unconnected {@link AccessPoint}s? * @param offset the offset to use for the {@link Brick} with less {@link AccessPoint}s. If the offset is too big * the maximum that can be used without reducing the amount of connected {@link Wire}s is used. * @return {@link Pair} of the actually used offset and the created {@link Wire}s. */ public static Pair<Integer, Set<Element>> createWiresforBricks(Brick firstBrick, Brick secondBrick, boolean onlyEmpty, int offset) { int[] intResults = new int[3]; Pair<List<AccessPoint>, List<AccessPoint>> listPair = connectBricksHelper(firstBrick, secondBrick, onlyEmpty, offset, intResults); Set<Element> wires = new LinkedHashSet<Element>(); for(int i = 0; i < intResults[0]; i++) { wires.add(new WireImpl(listPair.getLeft().get(i+intResults[1]), listPair.getRight().get(i+intResults[2]))); } return new Pair<Integer, Set<Element>>(Math.max(intResults[1], intResults[2]), wires); } /** * Helper to connect the {@link Output}s of the first {@link Brick} with the {@link Input}s of the second {@link Brick}. * This is done pairwise, starting with the topmost {@link AccessPoint}s. If one {@link Brick} has more * {@link AccessPoint}s than the other the remain ones are not connected. * @param firstBrick the first {@link Brick} to connect. Here the {@link Output}s get connected. * @param secondBrick the second {@link Brick} to connect. Here the {@link Input}s get connected. * @param onlyEmpty only connect unconnected {@link AccessPoint}s? * @param offset the offset to use for the {@link Brick} with less {@link AccessPoint}s. If the offset is too big * the maximum that can be used without reducing the amount of connected {@link Wire}s is used. * @param intResults int array to store the results. First is the amount of {@link AccessPoint}s to connect, * second is the offset for the first brick, third is the offset for the second brick. * @return {@link Pair} of the two {@link AccessPoint} lists. Left are the {@link Output}s of the * first {@link Brick}, right are the {@link Input}s of the second {@link Brick}. */ private static Pair<List<AccessPoint>, List<AccessPoint>> connectBricksHelper(Brick firstBrick, Brick secondBrick, boolean onlyEmpty, int offset, int[] intResults) { assert intResults.length == 3; List<AccessPoint> outputs = new ArrayList<AccessPoint>(firstBrick.getOutputs()); List<AccessPoint> inputs = new ArrayList<AccessPoint>(secondBrick.getInputs()); if (onlyEmpty) { filterConnectedAccessPoints(outputs); filterConnectedAccessPoints(inputs); } Collections.sort(outputs, new VerticalThanHorizontalOrderComparatorForAccessPoints()); Collections.sort(inputs, new VerticalThanHorizontalOrderComparatorForAccessPoints()); int amount = Math.min(outputs.size(), inputs.size()); int firstOffset = 0; int secondOffset = 0; offset = Math.min(offset, Math.abs(inputs.size() - outputs.size())); if (outputs.size() > inputs.size()) { firstOffset = offset; } else { secondOffset = offset; } intResults[0] = amount; intResults[1] = firstOffset; intResults[2] = secondOffset; return new Pair<List<AccessPoint>, List<AccessPoint>>(outputs, inputs); } /** * Removes all {@link AccessPoint}s that have {@link Wire}s connected from the given {@link Collection}. * @param points the {@link Collection} of {@link AccessPoint}s to filter. */ private static void filterConnectedAccessPoints(Collection<AccessPoint> points) { for(Iterator<AccessPoint> i = points.iterator(); i.hasNext();) { if (i.next().getWires().size() != 0) { i.remove(); } } } /** * like {@link #getTotalDelayOfCombinatorialCircuit(Set, Set)} but finds the * switches and lights by its own. * */ public static long getTotalDelayOfCombinatorialCircuit(final Circuit c) { final Set<Switch> switches = new HashSet<Switch>(); final Set<Light> lights = new HashSet<Light>(); new ElementDispatcher() { { for(Element elem : c.getElements()) { elem.dispatch(this); } } @Override public void visit(IdPoint idPoint) { } @Override public void visit(Not not) { } @Override public void visit(Or or) { } @Override public void visit(And and) { } @Override public void visit(Light light) { lights.add(light); } @Override public void visit(Switch s) { switches.add(s); } @Override public void visit(Component component) { } @Override public void visit(Wire wire) { } @Override public void visit(Element element) { } }; return getTotalDelayOfCombinatorialCircuit(switches, lights); } /** * Get the total delay of a combinatorial circuit. Strating from switches, * ending at lights * */ public static long getTotalDelayOfCombinatorialCircuit(Set<Switch> switches, Set<Light> lights) { Set<Input> stopInputs = new HashSet<Input>(); for (Switch sw : switches) { Output output = sw.getOutputs().iterator().next(); for(Wire wire : output.getWires()) { Input input = (Input) wire.getOther(output); stopInputs.add(input); } } Set<Output> startOutputs = new HashSet<Output>(); for (Light light : lights) { Input input = light.getInputA(); if (!input.getWires().isEmpty()) { Output output = (Output) input.getWires().iterator().next().getOther(input); // filter direct switch light connections if (!stopInputs.contains(input)) { startOutputs.add(output); } } } return getTotalDelayHelper(startOutputs, stopInputs); } /** * Helper for {@link #getTotalDelayOfCombinatorialCircuit(Set, Set)} * * @param startOutputs * @param stopInputs * @return */ private static long getTotalDelayHelper(Set<Output> startOutputs, Set<Input> stopInputs) { long currentDelay = 0; for (Output output : startOutputs) { Brick currentBrick = output.getBrick(); Set<Output> nextOutputs = new HashSet<Output>(); for (Input input : currentBrick.getInputs()) { if (!stopInputs.contains(input) && input.getWires().size() != 0) { nextOutputs.add((Output) input.getWires().iterator().next() .getOther(input)); } } long myDelay = currentBrick.getDelay(); if(currentBrick instanceof Component) { Component currentComponent = (Component) currentBrick; ComponentType currentType = currentComponent.getType(); Log.gl().debug("Entering: " + currentComponent.getType().getName()); myDelay += getTotalDelayHelper(currentType.getOutputs(), currentType.getInputs()); Log.gl().debug("Leaveing: " + currentComponent.getType().getName()); } currentDelay = max(currentDelay, myDelay + getTotalDelayHelper(nextOutputs, stopInputs)); } return currentDelay; } public static Brick findBrickAtSamePos(Circuit c, Point p) { for(Element e: c.getElements()) { if (e instanceof Brick && e.getPosition().equals(p)) { return ((Brick) e); } } return null; } }