Canvas.java 9.1 KB

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