VisualizationPanel.java 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574
  1. package de.tu_darmstadt.tk.SmartHomeNetworkSim.view;
  2. import java.awt.BasicStroke;
  3. import java.awt.Color;
  4. import java.awt.Graphics;
  5. import java.awt.Graphics2D;
  6. import java.awt.RenderingHints;
  7. import java.awt.Stroke;
  8. import java.awt.event.ComponentEvent;
  9. import java.awt.event.ComponentListener;
  10. import java.util.Collection;
  11. import java.util.HashMap;
  12. import java.util.Observable;
  13. import java.util.Observer;
  14. import javax.swing.JPanel;
  15. import de.tu_darmstadt.tk.SmartHomeNetworkSim.control.LinkColorController;
  16. import de.tu_darmstadt.tk.SmartHomeNetworkSim.control.SettingsController;
  17. import de.tu_darmstadt.tk.SmartHomeNetworkSim.control.Controller;
  18. import de.tu_darmstadt.tk.SmartHomeNetworkSim.control.NetworkController;
  19. import de.tu_darmstadt.tk.SmartHomeNetworkSim.core.Connection;
  20. import de.tu_darmstadt.tk.SmartHomeNetworkSim.core.Link;
  21. import de.tu_darmstadt.tk.SmartHomeNetworkSim.core.Port;
  22. import de.tu_darmstadt.tk.SmartHomeNetworkSim.core.SmartDevice;
  23. import de.tu_darmstadt.tk.SmartHomeNetworkSim.core.util.Pair;
  24. import de.tu_darmstadt.tk.SmartHomeNetworkSim.view.VisualizationInteractor;
  25. import de.tu_darmstadt.tk.SmartHomeNetworkSim.view.util.LinkToolTip;
  26. /**
  27. * Panel which visualizes the SmartHome Network, it's Devices and Connections
  28. *
  29. *
  30. * @author Andreas T. Meyer-Berg
  31. */
  32. @SuppressWarnings("serial")
  33. public class VisualizationPanel extends JPanel implements Observer {
  34. /**
  35. * Controller to notify when the model changes
  36. */
  37. private Controller control;
  38. /**
  39. * Settings Controller
  40. */
  41. private SettingsController config;
  42. /**
  43. * Network Controller
  44. */
  45. private NetworkController network;
  46. /**
  47. * Listener which processes the GUI Interactions
  48. */
  49. private VisualizationInteractor interactor;
  50. /**
  51. * Initializes the Visualization Panel
  52. *
  53. * @param control
  54. * Control, which changes the model
  55. */
  56. public VisualizationPanel(Controller control) {
  57. super();
  58. this.control = control;
  59. this.config = control.getSettingsController();
  60. this.network = control.getNetworkController();
  61. this.interactor = new VisualizationInteractor(control, this);
  62. this.addMouseMotionListener(interactor);
  63. this.addMouseListener(interactor);
  64. this.addKeyListener(interactor);
  65. }
  66. /**
  67. * Performs further initializations, which have to be performed, after the
  68. * model was made visible
  69. */
  70. public void delayedInit() {
  71. this.addComponentListener(new ComponentListener() {
  72. @Override
  73. public void componentShown(ComponentEvent e) {
  74. }
  75. @Override
  76. public void componentResized(ComponentEvent e) {
  77. config.setDimension(getWidth(), getHeight(), control.getSettingsController().getDepth(),
  78. true);
  79. repaint();
  80. }
  81. @Override
  82. public void componentMoved(ComponentEvent e) {
  83. }
  84. @Override
  85. public void componentHidden(ComponentEvent e) {
  86. }
  87. });
  88. config.setDimension(getWidth(), getHeight(), control.getSettingsController().getDepth(),
  89. true);
  90. repaint();
  91. }
  92. @Override
  93. public void paint(Graphics g) {
  94. setRenderOptions(g);
  95. // paint white background
  96. g.setColor(Color.white);
  97. g.fillRect(0, 0, this.getWidth(), this.getHeight());
  98. if(config.isShowConnections())
  99. paintConnections(g);
  100. if(config.isShowSmartDevices())
  101. paintDevices(g);
  102. paintDrag(g);
  103. paintToolTip(g);
  104. paintDebugInformation(g);
  105. paintLinkLabels(g);
  106. }
  107. /**
  108. * Paints debug information like the current interaction mode
  109. * @param g Graphics which should be used
  110. */
  111. private void paintDebugInformation(Graphics g) {
  112. if(!config.isDebugModus())
  113. return;
  114. /**
  115. * Visual representation of the interaction modus
  116. */
  117. String modus;
  118. switch (interactor.mode) {
  119. case VisualizationInteractor.DRAG_CONNECTION:
  120. modus="Drag Connection";
  121. break;
  122. case VisualizationInteractor.SELECTED_DRAG:
  123. modus="Drag Device";
  124. break;
  125. case VisualizationInteractor.DRAG_SELECT:
  126. modus="Drag Select";
  127. break;
  128. case VisualizationInteractor.NOTHING:
  129. modus="Nothing";
  130. break;
  131. case VisualizationInteractor.RIGHTCLICK_MENU:
  132. modus="RightClick Menu";
  133. break;
  134. case VisualizationInteractor.SELECTED:
  135. modus="Selected";
  136. break;
  137. default:
  138. modus="Unknown";
  139. break;
  140. }
  141. g.setColor(Color.RED);
  142. g.drawString("Debug(Modus:"+modus+")", 0, 10);
  143. if(interactor.controlDown)
  144. g.drawString("Control down", 0, 20);
  145. }
  146. /**
  147. * Paints a list of all Link names on the left hand side
  148. * @param g Graphics to be used
  149. */
  150. private void paintLinkLabels(Graphics g) {
  151. if(!config.isShowLinks()||network.getLinks().isEmpty()||!config.isShowLinkNameList())
  152. return;
  153. g.setColor(Color.BLACK);
  154. /**
  155. * y-position of the next text Link line
  156. */
  157. int yPositionLink = 10;
  158. if(config.isDebugModus())
  159. yPositionLink+=25;
  160. g.drawString("Links:", 0, yPositionLink);
  161. yPositionLink += 10;
  162. for(Link l: network.getLinks()){
  163. if(!config.getNetworkTreeSettingsController().isVisible(l))continue;
  164. g.setColor(config.getLinkColors().getColorOfLink(l).getRight());
  165. g.drawString(l.getName(), 0, yPositionLink);
  166. yPositionLink+=10;
  167. }
  168. }
  169. /**
  170. * Sets the different rending options if available
  171. * @param g Graphics to be improved
  172. */
  173. private void setRenderOptions(Graphics g) {
  174. // More beautiful Graphics
  175. if(g instanceof Graphics2D){
  176. /**
  177. * Graphics2D options, if enabled on the machine
  178. */
  179. Graphics2D graphics = (Graphics2D) g;
  180. /**
  181. * Rendering Options for the visualization
  182. */
  183. RenderingHints renders = new RenderingHints(new HashMap<>());
  184. /**
  185. * Add AntiAliasing
  186. */
  187. renders.add(new RenderingHints(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON));
  188. /**
  189. * Add Text AntiAliasing
  190. */
  191. renders.add(new RenderingHints(RenderingHints.KEY_TEXT_ANTIALIASING,RenderingHints.VALUE_TEXT_ANTIALIAS_ON));
  192. /**
  193. * Add Improved Color Rendering
  194. */
  195. renders.add(new RenderingHints(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY));
  196. /**
  197. * Add general quality Rendering
  198. */
  199. renders.add(new RenderingHints(RenderingHints.KEY_RENDERING,RenderingHints.VALUE_RENDER_QUALITY));
  200. /**
  201. * Add improved interpolation for images
  202. */
  203. renders.add(new RenderingHints(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC));
  204. graphics.setRenderingHints(renders);
  205. }
  206. }
  207. /**
  208. * Paints the smart devices of the Model
  209. *
  210. * @param g
  211. * the Graphics object to visualize on
  212. */
  213. protected void paintDevices(Graphics g) {
  214. /**
  215. * Radius of devices
  216. */
  217. int deviceRadius = config.getDeviceVisualizationRadius();
  218. /*
  219. * Visualization calculations for all devices
  220. */
  221. /**
  222. * Number of Links in the Model
  223. */
  224. int numberOfLinks = network.getVisibleLinks().size();
  225. /**
  226. * Angle per Link in "linkSlice degrees"
  227. */
  228. double linkSlice = Math.min(360.0 / numberOfLinks,90.0);
  229. /**
  230. * Map of links to Colors and position of the Color
  231. */
  232. LinkColorController linkColors = config.getLinkColors();
  233. if(config.isShowLinks()){
  234. int i = 0;
  235. for(Link l: network.getVisibleLinks()){
  236. /**
  237. * Index & Color
  238. */
  239. Pair<Integer, Color> p = config.getLinkColors().getColorOfLink(l);
  240. p.setLeft(i);
  241. i++;
  242. }
  243. }
  244. /**
  245. * Original Stroke - if it was changed
  246. */
  247. Stroke f = null;
  248. /**
  249. * Set bigger brush size if possible
  250. */
  251. if(g instanceof Graphics2D){
  252. Graphics2D g2 = (Graphics2D) g;
  253. f = g2.getStroke();
  254. g2.setStroke(new BasicStroke(Math.max(deviceRadius/20.0f,1f)));
  255. }
  256. /**
  257. * Paint Devices
  258. */
  259. //Values for dragging of multiple Devices
  260. /**
  261. * Pixel offset of dragged devices on the x axis
  262. */
  263. int x_offset = 0;
  264. /**
  265. * Pixel offset of dragged devices on the y axis
  266. */
  267. int y_offset = 0;
  268. if(interactor.mode == VisualizationInteractor.SELECTED_DRAG && !control.getSettingsController().getConfigurationManager().getSelectionModel().selectedDevices.isEmpty()){
  269. x_offset = interactor.dragged_x-interactor.dragged.getX();
  270. y_offset = interactor.dragged_y-interactor.dragged.getY();
  271. }
  272. //Paint Devices
  273. for (SmartDevice s: network.getVisibleSmartDevices()) {
  274. /**
  275. * x Position of the device
  276. */
  277. int x = s.getX();
  278. /**
  279. * y Position of the device
  280. */
  281. int y = s.getY();
  282. if(interactor.mode==VisualizationInteractor.SELECTED_DRAG && control.getSettingsController().getConfigurationManager().getSelectionModel().selectedDevices.contains(s)) {
  283. // Update visualization of dragged object
  284. x += x_offset;
  285. y += y_offset;
  286. }
  287. //Paint Links of Device V0.0
  288. if(config.isShowLinks()){
  289. for(Link l:s.getLinks()){
  290. /*
  291. * Split circle around the object into NumberOfLinks Sections, color the sections, connected with the device
  292. */
  293. /**
  294. * Number and color of the current link
  295. */
  296. Pair<Integer, Color> linkPos = linkColors.getColorOfLink(l);
  297. if(linkPos==null||!config.getNetworkTreeSettingsController().isVisible(l))continue;//Skip Links, which are not in the model or hidden
  298. g.setColor(linkPos.getRight());
  299. g.fillArc(x - deviceRadius - config.getLinkRadius(), y - deviceRadius - config.getLinkRadius(),
  300. 2 * deviceRadius - 1 + 2 * config.getLinkRadius(), 2 * deviceRadius - 1 + 2*config.getLinkRadius(),
  301. (int)Math.round(linkPos.getLeft()*linkSlice), (int)Math.ceil(linkSlice));
  302. }
  303. }
  304. if(//(interactor.mode==VisualisationInteractor.SELECTED||interactor.mode==VisualisationInteractor.DRAG_SELECT ||interactor.mode == VisualisationInteractor.SELECTED_DRAG || interactor.mode == VisualisationInteractor.RIGHTCLICK_MENU)&&
  305. (control.getSettingsController().getConfigurationManager().getSelectionModel().selectedDevices.contains(s)^control.getSettingsController().getConfigurationManager().getSelectionModel().selectedDevicesDrag.contains(s))){
  306. //HighlightSelected Devices
  307. g.setColor(new Color(135,206,235));
  308. }else{
  309. g.setColor(Color.WHITE);
  310. }
  311. fillCircle(g, x, y, deviceRadius);
  312. g.setColor(Color.BLACK);
  313. drawCircle(g,x,y,deviceRadius);
  314. g.setColor(new Color(91, 162, 229));
  315. /**
  316. * Distance in pixels between the two circles at the border
  317. */
  318. int innerDistance = Math.max(2, Math.round(deviceRadius/13f));
  319. drawCircle(g, x, y, deviceRadius-innerDistance);
  320. g.setColor(Color.BLACK);
  321. if(config.isShowSmartDeviceNames())
  322. g.drawString(s.getName(),
  323. x - g.getFontMetrics().stringWidth(s.getName()) / 2, y
  324. + deviceRadius + 11 + (config.isShowLinks()?config.getLinkRadius():0));
  325. }
  326. if(f!=null)
  327. ((Graphics2D) g).setStroke(f);
  328. }
  329. /**
  330. * Paints the Connections
  331. *
  332. * @param g
  333. * the Graphics object to visualize on
  334. */
  335. protected void paintConnections(Graphics g) {
  336. //Values for dragging of multiple Devices
  337. int x_offset = 0;
  338. int y_offset = 0;
  339. if(interactor.mode == VisualizationInteractor.SELECTED_DRAG && !control.getSettingsController().getConfigurationManager().getSelectionModel().selectedDevices.isEmpty()){
  340. x_offset = interactor.dragged_x-interactor.dragged.getX();
  341. y_offset = interactor.dragged_y-interactor.dragged.getY();
  342. }
  343. // For all Connections
  344. for (Connection c : network.getVisibleConnections()) {
  345. Color connectionState;
  346. switch (c.getStatus()) {
  347. case Connection.ACTIVE:
  348. connectionState = Color.GREEN;
  349. break;
  350. case Connection.HALTED:
  351. connectionState = Color.ORANGE;
  352. break;
  353. case Connection.DONE:
  354. connectionState = Color.GRAY;
  355. break;
  356. case Connection.TERMINATED:
  357. case Connection.FINISHED:
  358. connectionState = Color.RED;
  359. break;
  360. default:
  361. connectionState = Color.BLUE;
  362. break;
  363. }
  364. g.setColor(connectionState);
  365. /**
  366. * All Devices that are part of the connection
  367. */
  368. Collection<Port> d = c.getParticipants();
  369. if(d.size()==0){
  370. System.out.println("WARNING: Empty, undeleted Connection: "+c.toString());
  371. continue;
  372. }
  373. if(c.getProtocol()==null){
  374. System.out.println("WARNING: Protocol is null: "+c.toString());
  375. continue;
  376. }
  377. for(Pair<Port,Port> p: c.getProtocol().getTopology() ){
  378. if(p.getLeft() != null && p.getLeft().getOwner() != null && p.getRight() != null && p.getRight().getOwner() != null){
  379. if(control.getSettingsController().getConfigurationManager().getSelectionModel().clickedConnection.contains(new Pair<Connection, Pair<Port,Port>>(c, p)))
  380. g.setColor(Color.BLUE);
  381. else
  382. g.setColor(connectionState);
  383. SmartDevice from = p.getLeft().getOwner();
  384. SmartDevice to = p.getRight().getOwner();
  385. if(!config.getNetworkTreeSettingsController().isVisible(from)||!config.getNetworkTreeSettingsController().isVisible(to))continue;
  386. int xFrom = from.getX();
  387. int yFrom = from.getY();
  388. int xTo = to.getX();
  389. int yTo = to.getY();
  390. if(control.getSettingsController().getConfigurationManager().getSelectionModel().selectedDevices.contains(from)^control.getSettingsController().getConfigurationManager().getSelectionModel().selectedDevicesDrag.contains(from)){
  391. xFrom+=x_offset;
  392. yFrom+=y_offset;
  393. }
  394. if(control.getSettingsController().getConfigurationManager().getSelectionModel().selectedDevices.contains(to)^control.getSettingsController().getConfigurationManager().getSelectionModel().selectedDevicesDrag.contains(to)){
  395. xTo+=x_offset;
  396. yTo+=y_offset;
  397. }
  398. g.drawLine(xFrom, yFrom, xTo, yTo);
  399. }
  400. }
  401. for(Pair<Port,Port> p: c.getProtocol().getDeletedTopology() ){
  402. if(control.getSettingsController().getConfigurationManager().getSelectionModel().clickedConnection.contains(new Pair<Connection, Pair<Port,Port>>(c, p)))
  403. g.setColor(Color.BLUE);
  404. else{
  405. //Just paint terminated connection, if config says so
  406. if(config.isShowTerminatedConnections())
  407. g.setColor(Color.RED);
  408. else
  409. continue;
  410. }
  411. if(p.getLeft() != null && p.getLeft().getOwner() != null && p.getRight() != null && p.getRight().getOwner() != null){
  412. SmartDevice from = p.getLeft().getOwner();
  413. SmartDevice to = p.getRight().getOwner();
  414. int xFrom = from.getX();
  415. int yFrom = from.getY();
  416. int xTo = to.getX();
  417. int yTo = to.getY();
  418. if(control.getSettingsController().getConfigurationManager().getSelectionModel().selectedDevices.contains(from)^control.getSettingsController().getConfigurationManager().getSelectionModel().selectedDevicesDrag.contains(from)){
  419. xFrom+=x_offset;
  420. yFrom+=y_offset;
  421. }
  422. if(control.getSettingsController().getConfigurationManager().getSelectionModel().selectedDevices.contains(to)^control.getSettingsController().getConfigurationManager().getSelectionModel().selectedDevicesDrag.contains(to)){
  423. xTo+=x_offset;
  424. yTo+=y_offset;
  425. }
  426. g.drawLine(xFrom, yFrom, xTo, yTo);
  427. }
  428. }
  429. }
  430. // paint new in progress connection, if a connection or link is in
  431. // creation
  432. if (interactor.mode == VisualizationInteractor.DRAG_CONNECTION && interactor.connectionFrom != null){
  433. g.setColor(Color.ORANGE);
  434. g.drawLine(interactor.connectionFrom.getX(),
  435. interactor.connectionFrom.getY(), interactor.dragged_x,
  436. interactor.dragged_y);
  437. }
  438. }
  439. /**
  440. * Paints the new dragged Connection
  441. * @param g graphics of this panel
  442. */
  443. protected void paintDrag(Graphics g){
  444. if(interactor.mode!=VisualizationInteractor.DRAG_SELECT)
  445. return;
  446. int low_x = Math.min(interactor.dragged_x, interactor.dragged_x_start);
  447. int low_y = Math.min(interactor.dragged_y, interactor.dragged_y_start);
  448. int high_x = Math.max(interactor.dragged_x, interactor.dragged_x_start);
  449. int high_y = Math.max(interactor.dragged_y, interactor.dragged_y_start);
  450. g.setColor(Color.BLUE);
  451. g.drawRect(low_x, low_y, high_x-low_x, high_y-low_y);
  452. }
  453. /**
  454. * Paints the link ToolTip
  455. * @param g graphics of this panel
  456. */
  457. protected void paintToolTip(Graphics g) {
  458. /**
  459. * ToolTip which is visualized
  460. */
  461. LinkToolTip toolTip = interactor.getToolTip();
  462. if (toolTip.isEnabled() && config.isShowLinkToolTips()) {
  463. /**
  464. * free space around the ToolTipText
  465. */
  466. int border = 2;
  467. /**
  468. * Width of the text (multiLine later ?)
  469. */
  470. int textWidth = g.getFontMetrics().stringWidth(toolTip.getText()) + 2 * border;
  471. /**
  472. * Height of the Text
  473. */
  474. int textHeigth = 12 + 2 * border;
  475. /**
  476. * top left x position of the ToolTip
  477. */
  478. int fixXPos = toolTip.getX();
  479. /**
  480. * top left y position of the ToolTip
  481. */
  482. int fixYPos = toolTip.getY();
  483. //Check position that toolTip stays in bound
  484. if (fixXPos < 0) {
  485. fixXPos = 0;
  486. } else if (fixXPos + textWidth >= this.getWidth()) {
  487. fixXPos -= (fixXPos + textWidth + 1) - this.getWidth();
  488. }
  489. if (fixYPos < 0){
  490. fixYPos = 0;
  491. }
  492. else if(fixYPos + textHeigth >= this.getHeight()) {
  493. fixYPos -= (fixYPos + textHeigth + 1) - this.getHeight();
  494. }
  495. //Paint background (toolTip Color (light yellow/brown))
  496. g.setColor(new Color(255, 255, 225));
  497. g.fillRect(fixXPos, fixYPos, textWidth, textHeigth);
  498. //Paint black surrounding rectangle
  499. g.setColor(Color.BLACK);
  500. g.drawRect(fixXPos, fixYPos, textWidth, textHeigth);
  501. //Draw the ToolTip text
  502. g.drawString(toolTip.getText(), fixXPos + 2, fixYPos + 12);
  503. }
  504. }
  505. @Override
  506. public void update(Observable o, Object arg) {
  507. repaint();
  508. }
  509. /**
  510. * Fills a circle of radius radius around the given point (Radius includes middle), So width may be 2*radius-1
  511. * @param g Graphics to be used
  512. * @param x x Position of the middle
  513. * @param y y Position of the middle
  514. * @param radius radius of the circle
  515. */
  516. public void fillCircle(Graphics g, int x, int y, int radius){
  517. g.fillOval(x-radius, y-radius, 2*radius+1, 2*radius+1);
  518. }
  519. /**
  520. * Draws a circle of radius radius around the given point (Radius includes middle)
  521. * @param g Graphics to be used
  522. * @param x x Position of the middle
  523. * @param y y Position of the middle
  524. * @param radius radius of the circle
  525. */
  526. public void drawCircle(Graphics g, int x, int y, int radius){
  527. g.drawOval(x-radius, y-radius, 2*radius-1, 2*radius-1);
  528. }
  529. }