Canvas.java 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. package holeg.ui.view.canvas;
  2. import holeg.model.*;
  3. import holeg.ui.controller.Control;
  4. import holeg.ui.model.GuiSettings;
  5. import holeg.utility.math.vector.Geometry;
  6. import holeg.utility.math.vector.Vec2i;
  7. import javax.swing.*;
  8. import java.awt.*;
  9. import java.awt.event.MouseEvent;
  10. import java.awt.event.MouseListener;
  11. import java.awt.event.MouseMotionListener;
  12. import java.util.HashSet;
  13. import java.util.Optional;
  14. import java.util.Set;
  15. import java.util.logging.Logger;
  16. public class Canvas extends JPanel {
  17. private static final Logger log = Logger.getLogger(Canvas.class.getName());
  18. private GroupNode groupNode;
  19. private final Control control;
  20. private final CanvasMouseListener canvasMouseListener = new CanvasMouseListener();
  21. private boolean enabled = true;
  22. public Canvas(Control control, GroupNode groupNode) {
  23. this.control = control;
  24. this.groupNode = groupNode;
  25. control.OnGuiSetEnabled.addListener(this::setCanvasEnabled);
  26. control.OnSelectionChanged.addListener(this::repaint);
  27. control.OnCanvasUpdate.addListener(this::repaint);
  28. this.setBackground(Color.WHITE);
  29. this.setPreferredSize(new Dimension(GuiSettings.canvasSize.getX(), GuiSettings.canvasSize.getY()));
  30. // TODO(Tom2022-01-14): remove listener when not needed anymore
  31. this.addMouseListener(canvasMouseListener);
  32. this.addMouseMotionListener(canvasMouseListener);
  33. }
  34. public void setGroupNode(GroupNode groupNode){
  35. this.groupNode = groupNode;
  36. }
  37. public static Rectangle getBoundsOfObject(AbstractCanvasObject obj) {
  38. int pictureScale = GuiSettings.getPictureScale();
  39. int pictureScaleDiv2 = GuiSettings.getPictureScaleDiv2();
  40. Vec2i pos = obj.getPosition();
  41. return new Rectangle(pos.getX() - pictureScaleDiv2, pos.getY() - pictureScaleDiv2, pictureScale, pictureScale);
  42. }
  43. private void setCanvasEnabled(boolean state) {
  44. enabled = state;
  45. }
  46. public GroupNode getGroupNode() {
  47. return this.groupNode;
  48. }
  49. @Override
  50. public void paintComponent(java.awt.Graphics g) {
  51. super.paintComponent(g);
  52. log.info("Draw");
  53. Graphics2D g2d = Rendering.initGraphics2D(g);
  54. Rendering.drawSelection(g2d);
  55. control.getModel().getEdgesOnCanvas().forEach(edge -> {
  56. if (edge.getA().getGroupNode().isEmpty() || edge.getB().getGroupNode().isEmpty()){
  57. return;
  58. }
  59. boolean edgeAinside = edge.getA().getGroupNode().get() == groupNode;
  60. boolean edgeBinside = edge.getB().getGroupNode().get() == groupNode;
  61. //both
  62. if (edgeAinside && edgeBinside) {
  63. Rendering.drawEdge(g2d, edge);
  64. }
  65. //TODO(Tom2022-01-16): oneside
  66. //none
  67. });
  68. groupNode.getHolonObjects().forEach(hO -> Rendering.drawHolonObject(g2d, hO));
  69. groupNode.getSwitches().forEach(hS -> Rendering.drawSwitchObject(g2d, hS));
  70. groupNode.getGroupNodes().forEach(groupNode -> Rendering.drawGroupNode(g2d, groupNode));
  71. groupNode.getNodes().forEach(node -> Rendering.drawNode(g2d, node));
  72. switch (canvasMouseListener.state) {
  73. case BoxSelection -> Rendering.drawSelectionBox(g2d, canvasMouseListener.getRectangleOfSelectionBox());
  74. case EdgeCreation -> Rendering.drawNewEdgeLine(g2d, canvasMouseListener.selectedOnPressed.getPosition(), canvasMouseListener.lastPosition);
  75. }
  76. }
  77. private Optional<AbstractCanvasObject> getObjectAtPosition(Vec2i pos) {
  78. return groupNode.getObjectsInThisLayer().filter(obj ->
  79. getBoundsOfObject(obj).contains(pos.getX(), pos.getY())
  80. ).findAny();
  81. }
  82. //TODO(Tom2022-01-17): checkForReplacement replace with getObjectAtPosition
  83. public Optional<AbstractCanvasObject> checkForReplacement(int x, int y) {
  84. return Optional.empty();
  85. }
  86. /**
  87. * Microsoft Windows10 selection & dragging behavior
  88. */
  89. private class CanvasMouseListener implements MouseListener, MouseMotionListener {
  90. private Vec2i lastPosition = new Vec2i();
  91. private Vec2i pressedPosition = new Vec2i();
  92. private Set<AbstractCanvasObject> selectionBeforeBoxSelection = new HashSet<>();
  93. private State state = State.None;
  94. private AbstractCanvasObject selectedOnPressed = null;
  95. @Override
  96. public void mousePressed(MouseEvent e) {
  97. if (!enabled) {
  98. return;
  99. }
  100. log.info(state.toString());
  101. if (!e.isControlDown()) {
  102. GuiSettings.getSelectedObjects().clear();
  103. }
  104. Vec2i pos = new Vec2i(e.getPoint());
  105. getObjectAtPosition(pos).ifPresentOrElse(obj -> {
  106. if (!GuiSettings.getSelectedObjects().contains(obj)) {
  107. state = State.Selection;
  108. GuiSettings.getSelectedObjects().add(obj);
  109. }
  110. selectedOnPressed = obj;
  111. }, () -> {
  112. state = State.BoxSelection;
  113. selectionBeforeBoxSelection = Set.copyOf(GuiSettings.getSelectedObjects());
  114. });
  115. control.OnSelectionChanged.broadcast();
  116. lastPosition = pressedPosition = pos;
  117. }
  118. @Override
  119. public void mouseDragged(MouseEvent e) {
  120. if (!enabled) {
  121. return;
  122. }
  123. log.info(state.toString());
  124. Vec2i actualPos = new Vec2i(e.getPoint());
  125. switch (state) {
  126. case None, Selection -> {
  127. // Not handle to small mouse dragging
  128. if (!(pressedPosition.getSquaredDistance(actualPos) > GuiSettings.dragThresholdDistance)) {
  129. return;
  130. }
  131. if (SwingUtilities.isLeftMouseButton(e)) {
  132. state = State.ObjectDragging;
  133. } else if (SwingUtilities.isRightMouseButton(e)) {
  134. state = State.EdgeCreation;
  135. }
  136. }
  137. case BoxSelection -> {
  138. Rectangle selectionBox = getRectangleOfSelectionBox();
  139. groupNode.getObjectsInThisLayer().forEach(obj -> {
  140. Rectangle bounds = getBoundsOfObject(obj);
  141. if (selectionBox.intersects(bounds) ^ selectionBeforeBoxSelection.contains(obj)) {
  142. control.addSelectedObject(obj);
  143. } else {
  144. control.removeObjectFromSelection(obj);
  145. }
  146. });
  147. repaint();
  148. }
  149. case ObjectDragging -> {
  150. Vec2i delta = actualPos.subtract(lastPosition);
  151. GuiSettings.getSelectedObjects().forEach(obj -> obj.getPosition().addAssign(delta));
  152. repaint();
  153. }
  154. case EdgeCreation -> repaint();
  155. }
  156. lastPosition = actualPos;
  157. }
  158. @Override
  159. public void mouseReleased(MouseEvent e) {
  160. if (!enabled) {
  161. return;
  162. }
  163. log.info(state.toString());
  164. switch (state) {
  165. case None -> {
  166. if (GuiSettings.getSelectedObjects().contains(selectedOnPressed)) {
  167. control.removeObjectFromSelection(selectedOnPressed);
  168. } else {
  169. control.addSelectedObject(selectedOnPressed);
  170. }
  171. }
  172. case EdgeCreation -> {
  173. getObjectAtPosition(lastPosition).ifPresentOrElse(obj -> {
  174. if(control.addEdgeOnCanvas(new Edge(selectedOnPressed, obj, GuiSettings.maxCapacityForNewCreatedEdges))){
  175. control.calculateStateAndVisualForCurrentTimeStep();
  176. }
  177. }, () -> {
  178. Node node = new Node("Node");
  179. groupNode.add(node);
  180. node.setPosition(new Vec2i(lastPosition));
  181. control.addEdgeOnCanvas(new Edge(selectedOnPressed, node, GuiSettings.maxCapacityForNewCreatedEdges));
  182. control.calculateStateAndVisualForCurrentTimeStep();
  183. });
  184. }
  185. }
  186. state = State.None;
  187. repaint();
  188. }
  189. @Override
  190. public void mouseClicked(MouseEvent e) {
  191. boolean doubleLeftClick = e.getClickCount() % 2 == 0 && SwingUtilities.isLeftMouseButton(e);
  192. if (doubleLeftClick) {
  193. log.info(state.toString());
  194. getObjectAtPosition(new Vec2i(e.getPoint())).ifPresent(obj -> {
  195. if (obj instanceof HolonSwitch sw) {
  196. sw.setMode(HolonSwitch.SwitchMode.Manual);
  197. sw.flipManualState();
  198. control.calculateStateAndVisualForCurrentTimeStep();
  199. }
  200. });
  201. }
  202. }
  203. Rectangle getRectangleOfSelectionBox() {
  204. return Geometry.createRectangleFromCorners(lastPosition, pressedPosition);
  205. }
  206. @Override
  207. public void mouseEntered(MouseEvent e) {
  208. }
  209. @Override
  210. public void mouseExited(MouseEvent e) {
  211. }
  212. @Override
  213. public void mouseMoved(MouseEvent e) {
  214. }
  215. private enum State {
  216. None, BoxSelection, EdgeCreation, ObjectDragging, Selection
  217. }
  218. }
  219. }