package ui.view.holarchy; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Stroke; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.geom.Ellipse2D; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JSeparator; import javax.swing.JSlider; import javax.swing.SwingConstants; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import classes.Holon; import ui.controller.Control; import util.Random; public class HolarchyPanel extends JPanel { Control control; HolarchyWindow parentFrame; JLabel layerLabelTitle; JSlider slider; JSeparator sep, sep2; int displayedLayer; final int minRadius = 24; int radius = minRadius; Holon root; ArrayList>> ellipsesChild; ArrayList> ellipses; ArrayList> layeredHolons; ArrayList surroundings; Map coloredHolons; public HolarchyPanel(HolarchyWindow parentFrame, Control control) { this.control = control; this.parentFrame = parentFrame; this.root = control.getModel().getStateHolon(); this.setBounds(0, 0, parentFrame.getWidth()*4/5, parentFrame.getHeight()); this.setLayout(null); this.setBackground(Color.white); this.addMouseListener(mouseListener); this.layerLabelTitle = new JLabel("Layer"); this.layerLabelTitle.setBounds(5, 5, 50, 20); this.layerLabelTitle.setHorizontalAlignment(SwingConstants.CENTER); this.add(this.layerLabelTitle); this.slider = new JSlider(); this.slider.setOrientation(SwingConstants.VERTICAL); this.slider.setBounds(5, 30, 50, 200); this.slider.setPaintLabels(true); this.slider.setPaintTicks(true); this.slider.setSnapToTicks(true); this.slider.addChangeListener(this.sliderChangeListener); this.slider.setValue(0); this.slider.setInverted(true); this.slider.setFocusable(false); this.slider.setBackground(Color.white); this.add(slider); this.sep = new JSeparator(SwingConstants.VERTICAL); this.sep.setBounds(60, 0, 1, 235); this.add(this.sep); this.sep2 = new JSeparator(SwingConstants.HORIZONTAL); this.sep2.setBounds(0, 235, 60, 1); this.add(this.sep2); this.ellipses = new ArrayList>(); this.ellipsesChild = new ArrayList>>(); this.displayedLayer = 0; this.layeredHolons = new ArrayList>(); this.surroundings = new ArrayList(); this.coloredHolons = new HashMap(); getAllHolons(); setSliderMax(this.layeredHolons.size()); createAllLayers(); generateColorSet(); } public void update() { ArrayList> layeredHolonsCpy = new ArrayList>(); copyList(layeredHolonsCpy, this.layeredHolons); this.layeredHolons.clear(); getAllHolons(); //the holarchy did not change if(compareAllHolons(layeredHolonsCpy)) return; this.ellipses = new ArrayList>(); this.ellipsesChild = new ArrayList>>(); this.surroundings = new ArrayList(); this.coloredHolons = new HashMap(); setSliderMax(this.layeredHolons.size()); createAllLayers(); generateColorSet(); repaint(); } public void paintComponent(Graphics g) { super.paintComponent(g); paintLayer(g, this.displayedLayer); } public void displayLayer(int layer) { this.displayedLayer = layer; this.repaint(); } public void paintLayer(Graphics g, int layer) { this.displayedLayer = layer; Graphics2D g2 = (Graphics2D) g; Stroke stroke = new BasicStroke(3f); g2.setStroke(stroke); //resize panel Ellipse2D surrounding = this.surroundings.get(this.displayedLayer); if(surrounding.getWidth() > this.getWidth()) { this.setSize((int) surrounding.getWidth(), this.getHeight()); } if(surrounding.getHeight() > this.getHeight()) { this.setSize(this.getWidth(), (int) surrounding.getHeight()); } for(Ellipse2D e : this.ellipses.get(this.displayedLayer).keySet()) { //draw ellipse g2.setColor( this.coloredHolons.get(this.ellipses.get(this.displayedLayer).get(e)) ); g2.draw(e); //draw child ellipses and add them to global list for(Ellipse2D e2 : this.ellipsesChild.get(layer).get(e).keySet()) { g2.setColor( this.coloredHolons.get(this.ellipsesChild.get(layer).get(e).get(e2)) ); g2.draw(e2); } } } public void createAllLayers() { for(int layer=0; layer>()); //estimate the required area, center if it is smaller than the panels size this.radius = this.minRadius; Ellipse2D surrounding = estimateSurrounding(60, 0, layer, this.minRadius); double newX = surrounding.getX(), newY = surrounding.getY(); if(surrounding.getWidth() <= this.getWidth()) { double diff = this.getWidth() - surrounding.getWidth(); newX = diff/2; } if(surrounding.getHeight() <= this.getHeight()) { double diff = this.getHeight() - surrounding.getHeight(); newY = diff/2; } surrounding = new Ellipse2D.Double(newX, newY, surrounding.getWidth(), surrounding.getHeight()); this.surroundings.add(layer, surrounding); //create ellipse for each holon this.ellipses.add(layer, placeHolons(this.layeredHolons.get(layer), surrounding, true)); for(Ellipse2D e : this.ellipses.get(layer).keySet()) { //draw child ellipses and add them to global list Map ellipses2 = placeHolons(ellipses.get(layer).get(e).childHolons, e, false); this.ellipsesChild.get(layer).put(e, ellipses2); } } } /** * randomly place an ellipse for each holon in holons inside the given ellipse without overlapping * @param holons * @param surrounding * @param drawChildren * @return */ public Map placeHolons(List holons, Ellipse2D surrounding, boolean drawChildren) { double startX = surrounding.getX(); double startY = surrounding.getY(); double width = surrounding.getWidth(); double height = surrounding.getHeight(); Map shapes = new HashMap(); int abord = 0; int i = 0; while(shapes.size() < holons.size() && abord < 1000) { Holon h = holons.get(i); int radius = this.radius; if(drawChildren) radius = this.radius * (h.getChildCount()+1) + 10; // System.out.println("drawing holon with radius "+radius); //randomly place an ellipse for the holon double x = startX + (width * Math.random()); double y = startY + (height * Math.random()); //check if ellipse is inside the surrounding boolean overlap = !surrounding.contains(x, y, radius*2, radius*2); if(overlap) { abord++; continue; } Ellipse2D e = new Ellipse2D.Double(x-5, y-5, radius*2+10, radius*2+10); //check whether or not the random ellipse overlaps with an existing one for(Ellipse2D e2 : shapes.keySet()) { if(e.intersects(e2.getX()-5, e2.getY()-5, e2.getWidth()+10, e2.getHeight()+10)) { overlap = true; break; } } abord++; //if not add the ellipse to the already existing ones if(!overlap) { shapes.put(e, h); i++; abord = 0; } } return shapes; } public Ellipse2D estimateSurrounding(int x, int y, int layer, int radius) { if(this.layeredHolons == null || this.layeredHolons.size() <= layer) return new Ellipse2D.Double(x, y, Integer.MAX_VALUE, Integer.MAX_VALUE); int w = 0; for(Holon h : this.layeredHolons.get(layer)) { w += radius * (h.getChildCount()+1); } w = w * 4 + 10; if(w > this.getHeight() - 50) { int div = radius / (w/(this.getHeight()-50)); if(div == radius) div--; this.radius = div; return estimateSurrounding(x, y, layer, div ); } this.radius = radius; return new Ellipse2D.Double(x, y, w, w); } public void getAllHolons() { this.layeredHolons.add((ArrayList) this.root.childHolons.clone()); //TODO: potential infinit loop while(true) { //new layer ArrayList children = new ArrayList(); for(Holon h : this.layeredHolons.get(this.layeredHolons.size()-1)) { children.addAll((Collection) h.childHolons.clone()); } if(children.size() > 0) { this.layeredHolons.add(children); } else { break; } } } public void setSliderMax(int n) { if(this.slider == null || n < 0) return; this.slider.setMaximum(n-1); if(n < 11) { this.slider.setMajorTickSpacing(1); } else if (n < 101) { this.slider.setMajorTickSpacing(10); this.slider.setMinorTickSpacing(1); } else { this.slider.setMajorTickSpacing(100); this.slider.setMinorTickSpacing(10); } } /** * generate a distinct color for each Holon in each layer */ public void generateColorSet() { for(int layer=0; layer colors = new ArrayList(); float hue = Random.nextFloatInRange(0, 1); for(float i = 0; i < 360; i += 360 / numColors) { hue += 0.618033988749895f; float saturation = 0.5f; //90 + Random.nextFloatInRange(0, 10); float lightness = 0.95f; //50 + Random.nextFloatInRange(0, 10); Color c = Color.getHSBColor(hue%1, saturation, lightness); colors.add(c); } for(int j=0;j> list) { if(this.layeredHolons.size() != list.size()) return false; for(int i=0; i> dest, ArrayList> src) { for(int i=0; i()); for(int j=0; j