Canvas.java 9.4 KB

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