Browse Source

Enables GUI resizing and SmartDevice dragging

-Resizing shrinks/stretches the Device positions
-Drag device by left-clicking and moving the mouse
-Model and Controller was updated accordingly
-VisualisationInteractor reacts to and processes the UI interactions
Andreas T. Meyer-Berg 5 years ago
parent
commit
d1869ecd61

+ 0 - 2
src/main/java/de/tu_darmstadt/tk/SmartHomeNetworkSim/Main.java

@@ -34,8 +34,6 @@ public class Main {
 		m = new Model();
 		c = new Controller(m);
 	    v = new MainFrame(m, c);
-	    v.setVisible(true);
-
 	}
 
 }

+ 112 - 1
src/main/java/de/tu_darmstadt/tk/SmartHomeNetworkSim/control/Controller.java

@@ -1,6 +1,7 @@
 package de.tu_darmstadt.tk.SmartHomeNetworkSim.control;
 
 import de.tu_darmstadt.tk.SmartHomeNetworkSim.core.Model;
+import de.tu_darmstadt.tk.SmartHomeNetworkSim.core.SmartDevice;
 
 /**
  * Controller which allows interaction with the Model and Simulation
@@ -12,8 +13,118 @@ public class Controller {
 	 * {@link Model} which stores the smart home model
 	 */
 	Model model;
-	
+
 	public Controller(Model model) {
 		this.model = model;
 	}
+
+	/**
+	 * Moves the SmartDevice device to the specified location, if it exists
+	 * 
+	 * @param device
+	 *            the device to move
+	 * @param x
+	 *            new x position
+	 * @param y
+	 *            new y position
+	 * @param z
+	 *            new z postiion
+	 */
+	public void moveSmartDevice(SmartDevice device, int x, int y, int z) {
+		for (SmartDevice d : model.getDevices()) {
+			if (d == device) {
+				d.setX(x);
+				d.setY(y);
+				d.setZ(z);
+			}
+		}
+	}
+
+	/**
+	 * Sets the Dimension of the model
+	 * 
+	 * @param width
+	 *            the width to set
+	 * @param height
+	 *            the height to set
+	 * @param depth
+	 *            the depth to set
+	 */
+	public void setDimension(int width, int height, int depth) {
+		setDimension(width, height, depth, false);
+	}
+
+	/**
+	 * Sets the Dimension of the model and moves SmartDevices to new relative
+	 * positions
+	 * 
+	 * @param width
+	 *            the width to set
+	 * @param height
+	 *            the height to set
+	 * @param depth
+	 *            the depth to set
+	 * @param true
+	 *            if smartDevice positions should be stretched
+	 */
+	public void setDimension(int width, int height, int depth, boolean stretchModel) {
+		//Dont allow to small Models
+		if (width < 400)
+			width = 400;
+		if (height < 400)
+			height = 400;
+		if (depth < 400)
+			depth = 400;
+
+		if (stretchModel) {
+			/**
+			 * Factor that changes the x position. Example: 1 -> positions stay
+			 * the same 0.5 -> x position is halved 2 -> x position is doubled
+			 */
+			double width_factor = ((double) width) / ((double) model.getWidth());
+			/**
+			 * Factor that changes the y position. Example: 1 -> positions stay
+			 * the same 0.5 -> y position is halved 2 -> y position is doubled
+			 */
+			double height_factor = ((double) height) / ((double) model.getHeight());
+			/**
+			 * Factor that changes the z position. Example: 1 -> positions stay
+			 * the same 0.5 -> z position is halved 2 -> z position is doubled
+			 */
+			double depth_factor = ((double) depth) / ((double) model.getDepth());
+			// Update all device positions
+			for (SmartDevice d : model.getDevices()) {
+				if (width_factor != 1.0)
+					d.setX(scalePos(d.getX(), width_factor, width));
+				if (height_factor != 1.0)
+					d.setY(scalePos(d.getY(), height_factor, height));
+				if (depth_factor != 1.0)
+					d.setZ(scalePos(d.getZ(), depth_factor, depth));
+			}
+		}
+		model.setWidth(width);
+		model.setHeight(height);
+		model.setDepth(depth);
+	}
+	
+	/**
+	 * Calculates the new scaled position, which should radius < newPost < upperBound - radius
+	 * @param oldPosition previous Position that should be scaled
+	 * @param factor how much it is stretched/shrunk
+	 * @param upperBound upper Bound of the frame, which should not be exceeded
+	 * @return newPosition that was calculated
+	 */
+	private int scalePos(int oldPosition, double factor, int upperBound){
+		/**
+		 * New Position that should be validated and returned
+		 */
+		int newPosition = (int) Math.round(factor * oldPosition);
+		//Update if it moves out of the frame
+		if(newPosition<model.getDevice_visualization_radius())
+			newPosition = model.getDevice_visualization_radius();
+		if(newPosition>=upperBound-model.getDevice_visualization_radius())
+			newPosition = upperBound-model.getDevice_visualization_radius();
+		
+		return newPosition;
+	}
 }

+ 87 - 6
src/main/java/de/tu_darmstadt/tk/SmartHomeNetworkSim/core/Model.java

@@ -13,23 +13,48 @@ public class Model {
 	private List<SmartDevice> devices;
 	private List<Link> connectionNetworks;
 	
+	/**
+	 * Width of the smart home model, 0 <= device.x < width
+	 */
+	private int width;
+	
+	/**
+	 * Height of the smart home model, 0 <= device.y < height
+	 */
+	private int height;
+	
+	/**
+	 * Depth of the smart home model, 0 <= device.y < depth
+	 */
+	private int depth;
+	
+	/**
+	 * Radius (including the middlePoinnt) of a smartDevice
+	 * 
+	 */
+	private int device_visualization_radius = 17;
+	
 	/**
 	 * Initializes the Model, with 3 default devices for testing purposes
 	 */
 	public Model() {
 		
+		setWidth(640);
+		setHeight(480);
+		setDepth(480);
+		
 		devices = new ArrayList<SmartDevice>();
 		connectionNetworks = new ArrayList<Link>();
 		
 		SmartDevice A = new SmartDevice("SmartTV");		
-		A.setX(50);
-		A.setY(50);
+		A.setX(200);
+		A.setY(150);
 		SmartDevice B = new SmartDevice("SmartDoor");
-		B.setX(100);
-		B.setY(100);
+		B.setX(400);
+		B.setY(150);
 		SmartDevice C = new SmartDevice("SmartLight");
-		C.setX(50);
-		C.setY(100);
+		C.setX(300);
+		C.setY(300);
 		
 		addDevices(A);
 		addDevices(B);
@@ -64,4 +89,60 @@ public class Model {
 	public void addDevices(SmartDevice device) {
 		this.devices.add(device);
 	}
+
+	/**
+	 * @return the width
+	 */
+	public int getWidth() {
+		return width;
+	}
+
+	/**
+	 * @param width the width to set
+	 */
+	public void setWidth(int width) {
+		this.width = width;
+	}
+
+	/**
+	 * @return the height
+	 */
+	public int getHeight() {
+		return height;
+	}
+
+	/**
+	 * @param height the height to set
+	 */
+	public void setHeight(int height) {
+		this.height = height;
+	}
+
+	/**
+	 * @return the depth
+	 */
+	public int getDepth() {
+		return depth;
+	}
+
+	/**
+	 * @param depth the depth to set
+	 */
+	public void setDepth(int depth) {
+		this.depth = depth;
+	}
+
+	/**
+	 * @return the device_visualization_radius
+	 */
+	public int getDevice_visualization_radius() {
+		return device_visualization_radius;
+	}
+
+	/**
+	 * @param device_visualization_radius the device_visualization_radius to set
+	 */
+	public void setDevice_visualization_radius(int device_visualization_radius) {
+		this.device_visualization_radius = device_visualization_radius;
+	}	
 }

+ 29 - 34
src/main/java/de/tu_darmstadt/tk/SmartHomeNetworkSim/view/MainFrame.java

@@ -2,6 +2,8 @@ package de.tu_darmstadt.tk.SmartHomeNetworkSim.view;
 
 import java.awt.Dimension;
 import java.awt.Toolkit;
+import java.awt.event.WindowEvent;
+import java.awt.event.WindowStateListener;
 import java.io.File;
 import java.io.IOException;
 
@@ -12,7 +14,6 @@ import javax.swing.JMenuItem;
 
 import java.awt.BorderLayout;
 
-import javax.swing.JPanel;
 import javax.swing.SwingConstants;
 
 import de.tu_darmstadt.tk.SmartHomeNetworkSim.control.Controller;
@@ -26,7 +27,7 @@ import de.tu_darmstadt.tk.SmartHomeNetworkSim.core.Model;
 @SuppressWarnings("serial")
 public class MainFrame extends JFrame {
 	
-	private final JPanel panel; 
+	public final VisualisationPanel panel; 
 	
 	/**
 	 * Model of the smart home
@@ -48,8 +49,17 @@ public class MainFrame extends JFrame {
 		
 		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
 		getContentPane().setLayout(new BorderLayout(0, 0));
-		panel = new VisualisationPanel(m);
+		
+		//Set initial size of the frame
+		Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
+		int width = screenSize.width; 
+		int height = screenSize.height;
+		this.setBounds(width/8, height/8, 6*width/8, 6*height/8);
+		this.setMinimumSize(new Dimension(640, 480));
+		
+		panel = new VisualisationPanel(model, control);
 		getContentPane().add(panel, BorderLayout.CENTER);
+		//Update model Dimensions to visualisation size
 		
 		try {
 			/**
@@ -68,36 +78,21 @@ public class MainFrame extends JFrame {
 		mntmOptions.setHorizontalAlignment(SwingConstants.LEFT);
 		menuBar.add(mntmOptions);
 		
-		Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
-		int width = screenSize.width; 
-		int height = screenSize.height;
-		this.setBounds(width/8, height/8, 6*width/8, 6*height/8);
-		this.setMinimumSize(new Dimension(640, 480));
-	}
-
-	/*
-	public MainFrame() {
-		super();
-		this.setTitle("Smart Home Network Simulator");
-		this.setBackground(Color.WHITE);
-		try {
-			/**
-			 * Icon of the MainFrame UI
-			 *//*
-			this.setIconImage(ImageIO.read(new File("src/main/ressources/images/SmartHomeNetworkSim_icon.jpeg")));
-		} catch (IOException e) {
-			System.err.println("WARNING: Failed to load Icon image in MainFrame.java");
-			e.printStackTrace();
-		}
-		this.setBounds(0, 0, 1000, 1000);
-		this.setDefaultCloseOperation(EXIT_ON_CLOSE);
-		JMenuBar menu = new JMenuBar();
-		menu.add(new JMenuItem("Test1"));
-		menu.add(new JMenuItem("Test2"));
-		menu.add(new JMenuItem("Test3"));
-		menu.setVisible(true);
-		this.add(menu);
-		this.add(new VisualisationPanel());
+		this.setVisible(true);
+		//Set Dimension of the model to the JPanel size
+		control.setDimension(panel.getWidth(),panel.getHeight(),1000);	
+		panel.delayedInit();
 		
-	}*/
+		//Update components if window was maximized
+		this.addWindowStateListener(new WindowStateListener() {
+			@Override
+			public void windowStateChanged(WindowEvent e) {
+				if ((e.getNewState() & MainFrame.MAXIMIZED_BOTH) == MainFrame.MAXIMIZED_BOTH){
+					revalidate();
+					//panel.revalidate();
+					//panel.repaint();
+				}
+			}
+		});
+	}
 }

+ 136 - 0
src/main/java/de/tu_darmstadt/tk/SmartHomeNetworkSim/view/VisualisationInteractor.java

@@ -0,0 +1,136 @@
+package de.tu_darmstadt.tk.SmartHomeNetworkSim.view;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseMotionListener;
+
+import javax.swing.event.MouseInputListener;
+
+import de.tu_darmstadt.tk.SmartHomeNetworkSim.control.Controller;
+import de.tu_darmstadt.tk.SmartHomeNetworkSim.core.Model;
+import de.tu_darmstadt.tk.SmartHomeNetworkSim.core.SmartDevice;
+
+/**
+ * Listener which detects User Interaction with the {@link VisualisationPanel},
+ * stores interaction information, triggers the controller and helps
+ * visualisation of the interaction
+ * 
+ * @author Andreas T. Meyer-Berg
+ */
+public class VisualisationInteractor implements MouseInputListener, MouseMotionListener, ActionListener {
+
+	/**
+	 * Smart Home model which should be interacted with
+	 */
+	private Model model;
+
+	/**
+	 * Controller to notify when the model changes
+	 */
+	private Controller control;
+
+	/**
+	 * Panel which is observed
+	 */
+	private VisualisationPanel panel;
+
+	/**
+	 * SmartDevice that is dragged on Screen
+	 */
+	public SmartDevice dragged = null;
+
+	/**
+	 * Current x Position of the dragged device
+	 */
+	public int dragged_x;
+
+	/**
+	 * Current y Position of the dragged device
+	 */
+	public int dragged_y;
+
+	/**
+	 * 
+	 * @param model
+	 * @param controller
+	 */
+	public VisualisationInteractor(Model model, Controller controller, VisualisationPanel panel) {
+		this.model = model;
+		this.control = controller;
+		this.panel = panel;
+	}
+
+	@Override
+	public void mouseClicked(MouseEvent e) {
+
+	}
+
+	@Override
+	public void mousePressed(MouseEvent e) {
+		// Check if SmartDevice was clicked
+		for (SmartDevice d : model.getDevices()) {
+			if (Math.abs(d.getX() - e.getX()) < panel.getVisualisationRadius()
+					&& Math.abs(d.getY() - e.getY()) < panel.getVisualisationRadius()) {
+				// Recognize possible drag operation
+				if (e.getButton() == MouseEvent.BUTTON1) {
+					dragged = d;
+					dragged_x = d.getX();
+					dragged_y = d.getY();
+				}
+			}
+		}
+	}
+
+	@Override
+	public void mouseReleased(MouseEvent e) {
+		// Finish drag operation
+		if (dragged != null && e.getButton() == MouseEvent.BUTTON1
+				&& (dragged.getX() != dragged_x || dragged.getY() != dragged_y)) {
+			control.moveSmartDevice(dragged, dragged_x, dragged_y, dragged.getZ());
+			panel.repaint();
+		}
+		dragged = null;
+	}
+
+	@Override
+	public void mouseEntered(MouseEvent e) {
+
+	}
+
+	@Override
+	public void mouseExited(MouseEvent e) {
+
+	}
+
+	@Override
+	public void actionPerformed(ActionEvent e) {
+
+	}
+
+	@Override
+	public void mouseDragged(MouseEvent e) {
+		// move dragged object on screen along y Axis
+		if (e.getX() <= panel.getVisualisationRadius())
+			dragged_x = panel.getVisualisationRadius();
+		else if (e.getX() >= model.getWidth() - panel.getVisualisationRadius())
+			dragged_x = model.getWidth() - panel.getVisualisationRadius();
+		else
+			dragged_x = e.getX();
+		// move dragged object on screen along y Axis
+		if (e.getY() <= panel.getVisualisationRadius())
+			dragged_y = panel.getVisualisationRadius();
+		else if (e.getY() >= model.getHeight() - panel.getVisualisationRadius())
+			dragged_y = model.getHeight() - panel.getVisualisationRadius();
+		else
+			dragged_y = e.getY();
+		// repaint the drag
+		if (dragged != null)
+			panel.repaint();
+	}
+
+	@Override
+	public void mouseMoved(MouseEvent e) {
+	}
+
+}

+ 85 - 31
src/main/java/de/tu_darmstadt/tk/SmartHomeNetworkSim/view/VisualisationPanel.java

@@ -2,11 +2,12 @@ package de.tu_darmstadt.tk.SmartHomeNetworkSim.view;
 
 import java.awt.Color;
 import java.awt.Graphics;
-import java.awt.event.MouseEvent;
-import java.awt.event.MouseMotionListener;
+import java.awt.event.ComponentEvent;
+import java.awt.event.ComponentListener;
 
 import javax.swing.JPanel;
 
+import de.tu_darmstadt.tk.SmartHomeNetworkSim.control.Controller;
 import de.tu_darmstadt.tk.SmartHomeNetworkSim.core.Model;
 import de.tu_darmstadt.tk.SmartHomeNetworkSim.core.SmartDevice;
 
@@ -17,60 +18,113 @@ public class VisualisationPanel extends JPanel {
 	 * Smart Home model which is visualized
 	 */
 	private Model model;
-	
+
+	/**
+	 * Controller to notify when the model changes
+	 */
+	private Controller control;
+
+	/**
+	 * Listener which processes the GUI Interactions
+	 */
+	private VisualisationInteractor interactor;
+
+	/**
+	 * Radius of a circle around the SmartDevice (including middlepoint)
+	 */
+	private int visualisationRadius = 17;
+
 	/**
 	 * Initializes the Visualisation Panel
-	 * @param model Model to visualize
+	 * 
+	 * @param model
+	 *            Model to visualize
+	 * @param control
+	 *            Control, which changes the model
 	 */
-	public VisualisationPanel(Model model) {
+	public VisualisationPanel(Model model, Controller control) {
 		super();
+
 		this.model = model;
-		this.addMouseMotionListener(new MouseMotionListener() {
-			
+		this.control = control;
+
+		this.interactor = new VisualisationInteractor(model, control, this);
+
+		this.addMouseMotionListener(interactor);
+		this.addMouseListener(interactor);
+	}
+	
+	/**
+	 * Performs further initializations, which have to be performed, after the model was made visible
+	 */
+	public void delayedInit(){
+		this.addComponentListener(new ComponentListener() {
+
+			@Override
+			public void componentShown(ComponentEvent e) {
+			}
+
+			@Override
+			public void componentResized(ComponentEvent e) {
+				control.setDimension(getWidth(), getHeight(), model.getDepth(), true);
+				repaint();
+			}
+
 			@Override
-			public void mouseMoved(MouseEvent e) {
-				if( e != null && e.getPoint() != null){
-					SmartDevice move = model.getDevices().get(0);
-					if(e.getX()>15 && e.getX()<getWidth()-16)
-						move.setX(e.getX());
-					if(e.getY()>15 && e.getY()<getHeight()-16)
-						move.setY(e.getY());
-					repaint();
-				}
-				
+			public void componentMoved(ComponentEvent e) {
 			}
-			
+
 			@Override
-			public void mouseDragged(MouseEvent e) {
-				// TODO Auto-generated method stub
-				
+			public void componentHidden(ComponentEvent e) {
 			}
 		});
 	}
 	
 	@Override
 	public void paint(Graphics g) {
-		super.paint(g);
-		//paint white background
+		// paint white background
 		g.setColor(Color.white);
 		g.fillRect(0, 0, this.getWidth(), this.getHeight());
-		
+
 		paintDevices(g);
 	}
-	
+
 	/**
 	 * Paints the smart devices of the Model
+	 * 
 	 * @param g
 	 */
-	public void paintDevices(Graphics g){
-		
-		for(SmartDevice s:model.getDevices()){
+	public void paintDevices(Graphics g) {
+
+		for (SmartDevice s : model.getDevices()) {
+			int x = s.getX();
+			int y = s.getY();
+			if (s == interactor.dragged) {
+				// Update visualization of dragged object
+				x = interactor.dragged_x;
+				y = interactor.dragged_y;
+			}
 			g.setColor(Color.BLACK);
-			g.drawOval(s.getX()-16, s.getY()-16, 32, 32);
+			g.drawOval(x - visualisationRadius, y - visualisationRadius, 2*visualisationRadius-1, 2*visualisationRadius-1);
 			g.setColor(Color.BLUE);
-			g.drawOval(s.getX()-14, s.getY()-14, 28, 28);
+			g.drawOval(x - visualisationRadius+2, y - visualisationRadius+2, 2*visualisationRadius-5, 2*visualisationRadius-5);
 			g.setColor(Color.BLACK);
-			g.drawString(s.getName(), s.getX()-g.getFontMetrics().stringWidth(s.getName())/2, s.getY()+26);
+			g.drawString(s.getName(), x - g.getFontMetrics().stringWidth(s.getName()) / 2, y + visualisationRadius+11);
 		}
 	}
+
+	/**
+	 * @return the visualisationRadius
+	 */
+	public int getVisualisationRadius() {
+		return visualisationRadius;
+	}
+
+	/**
+	 * @param visualisationRadius the visualisationRadius to set
+	 */
+	public void setVisualisationRadius(int visualisationRadius) {
+		this.visualisationRadius = visualisationRadius;
+	}
+
 }