package ui.view; import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.RenderingHints; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; import java.awt.event.MouseWheelEvent; import java.awt.event.MouseWheelListener; import java.util.ArrayList; import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.plaf.BorderUIResource.BevelBorderUIResource; import classes.AbstractCpsObject; import classes.Constants; import classes.CpsUpperNode; import classes.HolonBody; import classes.HolonElement; import classes.HolonObject; import classes.SubNet; import classes.Vector2d; import ui.controller.Control; import ui.model.Model; public class HolonCanvas extends JPanel implements MouseWheelListener, MouseListener, MouseMotionListener { /** * */ private static final long serialVersionUID = 1L; // Rendering private Graphics2D g2; // Ball objects private ArrayList bodies = new ArrayList<>(); private int subCount; // Frames private int currentFrameRate; private long previousTime = System.currentTimeMillis(); private long currentTime = previousTime; private long elapsedTime; private long totalElapsedTime = 0; private int frameCount = 0; private Dimension center; private ArrayList subnets; private Control controller; private Model model; private int maxX; private int maxY; private JComboBox combo = new JComboBox<>(); private int comboChoice = 0; private String info; private int bodyNr; private final JLabel lblBodyInfo = new JLabel("Holon Info:"); private JCheckBox chckbxSort = new JCheckBox("sort by size"); private ArrayList sortedSize = new ArrayList<>(); private ArrayList sortedDist = new ArrayList<>(); private boolean sizeChange = false; private HolonBody toDrag; private boolean beingDragged = false; public HolonCanvas(Model mod, Control control) { // Wire up Events this.controller = control; this.model = mod; add(lblBodyInfo); this.add(combo); subnets = controller.getSimManager().getSubNets(); subCount = subnets.size(); previousTime = System.currentTimeMillis(); currentTime = previousTime; totalElapsedTime = 0; frameCount = 0; this.addMouseWheelListener(this); combo.addItem("ID"); combo.addItem("Nr. of Objects"); combo.addItem("Nr. of Edges"); combo.addItem("Nr. of Switches"); combo.addItem("Total Production"); combo.addItem("Total Consumption"); combo.addItem("Nr. of Elements"); combo.addItem("Nr. of Producers"); combo.addItem("Nr. of active Elements"); combo.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { comboChoice = combo.getSelectedIndex(); } }); this.addMouseListener(this); this.addMouseMotionListener(this); chckbxSort.addChangeListener(new ChangeListener() { @Override public void stateChanged(ChangeEvent e) { sizeChange = true; } }); add(chckbxSort); } // Start Render and Update Threads public void paintComponent(Graphics g) { super.paintComponent(g); // add new colors if necessary for (int i = 0; i < controller.getSimManager().getSubNets().size(); i++) { if (model.getSubNetColors().size() - 1 < i) { controller.addSubNetColor(new Color((int) (Math.random() * 255), (int) (Math.random() * 255), (int) (Math.random() * 255))); } } // check if subnets have changed if (!controller.getSimManager().getSubNets().equals(subnets)) { subnets = controller.getSimManager().getSubNets(); subCount = subnets.size(); calcCenter(); maxX = center.width; maxY = center.height; addNewBodies(subCount); sizeChange = true; } currentTime = System.currentTimeMillis(); elapsedTime = (currentTime - previousTime); // elapsed time in seconds totalElapsedTime += elapsedTime; if (totalElapsedTime > 1000) { currentFrameRate = frameCount; frameCount = 0; totalElapsedTime = 0; } // check if HolonBodys should bes sorted after size if (chckbxSort.isSelected() && sizeChange) { rearrangeAfterSize(); } updateBodies(elapsedTime / 1000f); render(g); try { // Thread.sleep(getFpsDelay(maxFrameRate)); Thread.sleep(5); } catch (Exception e) { e.printStackTrace(); } previousTime = currentTime; frameCount++; repaint(); } // sort the HolonBodys after size private void rearrangeAfterSize() { sizeChange = false; HolonBody tmp = null; int j = 0; sortedSize.addAll(bodies); insertionSizeSort(sortedSize); sortedDist.addAll(bodies); ArrayList pos = insertionDistSort(sortedDist); for (int i = 0; i < subCount; i++) { for (j = 0; j < subCount; j++) { if (sortedSize.get(subCount - 1 - i).getId() == bodies.get(j).getId()) { bodies.get(j).position = pos.get(i); break; } } } } // updates the bodies according to the changes of subnets private void addNewBodies(int subCount) { // find correct color for existing HolonBodys ArrayList newBodies = new ArrayList<>(); for (int i = 0; i < subCount; i++) { for (int j = 0; j < bodies.size(); j++) { if (model.getSubNetColors().get(i) == bodies.get(j).getColor()) { bodies.get(j).setRadius( (subnets.get(i).getObjects().size() * 5 + 10) * controller.getHolonBodyScale() / 100); bodies.get(j).setMass((float) Math.pow((subnets.get(i).getObjects().size() + 1) * 5, 3)); newBodies.add(bodies.get(j)); newBodies.get(i).setId(i); if (newBodies.get(i).position.getX() > maxX) maxX = (int) newBodies.get(i).position.getX(); if (newBodies.get(i).position.getY() > maxY) maxY = (int) newBodies.get(i).position.getY(); break; } } } bodies = newBodies; // adding new HolonBodys for (int i = bodies.size(); i < subCount; i++) { float radius = (subnets.get(i).getObjects().size() * 5 + 10) * controller.getHolonBodyScale() / 100; HolonBody temp = new HolonBody(maxX + 1, maxY + 1, radius, (float) Math.pow((subnets.get(i).getObjects().size() + 1) * 5, 3), model.getSubNetColors().get(i)); temp.setId(i); bodies.add(temp); if (bodies.get(i).position.getX() > maxX) maxX = (int) newBodies.get(i).position.getX(); if (bodies.get(i).position.getY() > maxY) maxY = (int) newBodies.get(i).position.getY(); } } public void render(Graphics g) { this.g2 = (Graphics2D) g; this.g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); // Render Background this.g2.setColor(Color.WHITE); this.g2.fillRect(0, 0, getWidth(), getHeight()); // Render Game Objects for (int i = 0; i < subCount; i++) { bodyNr = bodies.get(i).getId(); switch (comboChoice) { case 0: info = "" + bodyNr; break; case 1: info = "" + subnets.get(bodyNr).getObjects().size(); break; case 2: info = "" + subnets.get(bodyNr).getEdges().size(); break; case 3: info = "" + subnets.get(bodyNr).getSwitches().size(); break; case 4: info = "" + getTotalProduction(new ArrayList(subnets.get(bodyNr).getObjects())); break; case 5: info = "" + getTotalConsumption(new ArrayList(subnets.get(bodyNr).getObjects())); break; case 6: info = "" + getTotalElements(new ArrayList(subnets.get(bodyNr).getObjects())); break; case 7: info = "" + getTotalProducers(new ArrayList(subnets.get(bodyNr).getObjects())); break; case 8: info = "" + getActiveElements(new ArrayList(subnets.get(bodyNr).getObjects())); break; default: info = "" + bodyNr; break; } bodies.get(i).setRadius((subnets.get(bodies.get(i).getId()).getObjects().size() * 5 + 10) * controller.getHolonBodyScale() / 100); bodies.get(i).draw(this.g2, info); } } private int getActiveElements(ArrayList objects) { int val = 0; for (AbstractCpsObject obj : objects) { if (obj instanceof HolonObject) { for (HolonElement ele : ((HolonObject) obj).getElements()) { if (ele.getActive()) { val += 1; } } } else if (obj instanceof CpsUpperNode) { val += getTotalProduction(((CpsUpperNode) obj).getNodes()); } } return val; } private int getTotalProducers(ArrayList objects) { float val = 0; int prod = 0; int tStep = model.getCurIteration(); for (AbstractCpsObject obj : objects) { if (obj instanceof HolonObject) { for (HolonElement ele : ((HolonObject) obj).getElements()) { if (ele.getEnergyAt()[tStep] > 0 && ele.getActive()) { val += ele.getEnergyAt()[tStep] * ele.getAmount(); } } if (val > 0) prod += 1; } else if (obj instanceof CpsUpperNode) { val += getTotalProduction(((CpsUpperNode) obj).getNodes()); } } return prod; } private int getTotalElements(ArrayList objects) { int val = 0; for (AbstractCpsObject obj : objects) { if (obj instanceof HolonObject) { val += ((HolonObject) obj).getElements().size(); } else if (obj instanceof CpsUpperNode) { val += getTotalConsumption(((CpsUpperNode) obj).getNodes()); } } return val; } private float getTotalConsumption(ArrayList objects) { float val = 0; int tStep = model.getCurIteration(); for (AbstractCpsObject obj : objects) { if (obj instanceof HolonObject) { for (HolonElement ele : ((HolonObject) obj).getElements()) { if (ele.getEnergyAt()[tStep] < 0 && ele.getActive()) { val += ele.getEnergyAt()[tStep] * ele.getAmount(); } } } else if (obj instanceof CpsUpperNode) { val += getTotalConsumption(((CpsUpperNode) obj).getNodes()); } } return val; } private float getTotalProduction(ArrayList objects) { float val = 0; int tStep = model.getCurIteration(); for (AbstractCpsObject obj : objects) { if (obj instanceof HolonObject) { for (HolonElement ele : ((HolonObject) obj).getElements()) { if (ele.getEnergyAt()[tStep] > 0 && ele.getActive()) { val += ele.getEnergyAt()[tStep] * ele.getAmount(); } } } else if (obj instanceof CpsUpperNode) { val += getTotalProduction(((CpsUpperNode) obj).getNodes()); } } return val; } public void updateBodies(float elapsedSeconds) { // step the position of movable objects based off their velocity/gravity // and elapsedTime calcCenter(); for (int i = 0; i < subCount; i++) { if (!bodies.get(i).equals(toDrag) || (bodies.get(i).equals(toDrag)&& !beingDragged)) { bodies.get(i).position.setX( (float) (bodies.get(i).position.getX() + (bodies.get(i).velocity.getX() * (elapsedSeconds)) - ((bodies.get(i).position.getX() - center.getWidth()) / (50 + subCount)))); bodies.get(i).position.setY( (float) (bodies.get(i).position.getY() + (bodies.get(i).velocity.getY() * (elapsedSeconds)) - ((bodies.get(i).position.getY() - center.getHeight()) / (50 + subCount)))); if (Math.abs(bodies.get(i).velocity.getX()) < Constants.epsilon) bodies.get(i).velocity.setX(0); if (Math.abs(bodies.get(i).velocity.getY()) < Constants.epsilon) bodies.get(i).velocity.setY(0); } } checkCollisions(); } // Insertion sort for Sweep and Prune public void insertionSort(ArrayList a) { for (int p = 1; p < subCount; p++) { Comparable tmp = a.get(p); int j = p; for (; j > 0 && tmp.compareTo(a.get(j - 1)) < 0; j--) a.set(j, a.get(j - 1)); a.set(j, (HolonBody) tmp); } } // Insertion sort for subnet size private void insertionSizeSort(ArrayList a) { for (int p = 1; p < subCount; p++) { HolonBody tmp = a.get(p); int j = p; for (; j > 0 && tmp.compareSizeTo(a.get(j - 1)) < 0; j--) a.set(j, a.get(j - 1)); a.set(j, (HolonBody) tmp); } } // Insertion sort for HolonBody distance private ArrayList insertionDistSort(ArrayList a) { ArrayList pos = new ArrayList<>(); for (int p = 1; p < subCount; p++) { HolonBody tmp = a.get(p); int j = p; for (; j > 0 && tmp.compareDistTo(a.get(j - 1), center) < 0; j--) a.set(j, a.get(j - 1)); a.set(j, (HolonBody) tmp); } for (int i = 0; i < subCount; i++) pos.add(a.get(i).position); return pos; } public void checkCollisions() { insertionSort(bodies); for (int i = 0; i < subCount; i++) { // Ball to Ball collision for (int j = i + 1; j < subCount; j++) { if ((bodies.get(i).position.getX() + bodies.get(i).getRadius()) < (bodies.get(j).position.getX() - bodies.get(j).getRadius())) break; if ((bodies.get(i).position.getY() + bodies.get(i).getRadius()) < (bodies.get(j).position.getY() - bodies.get(j).getRadius()) || (bodies.get(j).position.getY() + bodies.get(j).getRadius()) < (bodies.get(i).position.getY() - bodies.get(i).getRadius())) continue; bodies.get(i).resolveCollision(bodies.get(j)); } } } // calc the center of the canvas public void calcCenter() { center = this.getSize(); center.height /= 2; center.width /= 2; } @Override public void mouseWheelMoved(MouseWheelEvent e) { controller.setHolonBodyScale(model.getHolonBodyScale() + (-e.getScrollAmount() * e.getWheelRotation())); } @Override public void mouseClicked(MouseEvent e) { // TODO Auto-generated method stub } @Override public void mouseEntered(MouseEvent e) { // TODO Auto-generated method stub } @Override public void mouseExited(MouseEvent e) { // TODO Auto-generated method stub } @Override public void mousePressed(MouseEvent e) { // Body Selection for (int i = 0; i < subCount; i++) { float cx = bodies.get(i).position.getX(); float cy = bodies.get(i).position.getY(); float cr = bodies.get(i).getRadius(); if (e.getX() - cr <= cx && e.getY() - cr <= cy && e.getX() + cr >= cx && e.getY() + cr >= cy) { controller.addSelectedHolonBody(bodies.get(i).getId()); toDrag = bodies.get(i); break; } else { controller.addSelectedHolonBody(-1); toDrag = null; } } } @Override public void mouseReleased(MouseEvent e) { beingDragged = false; } @Override public void mouseDragged(MouseEvent e) { beingDragged = true; if (toDrag != null) { toDrag.position = new Vector2d(e.getX(), e.getY()); } repaint(); } @Override public void mouseMoved(MouseEvent e) { // TODO Auto-generated method stub } }