From e7c8598d1d10c5d636cb58db350c0eb1f81c82d1 Mon Sep 17 00:00:00 2001 From: Vanya Date: Sun, 26 Mar 2023 22:32:31 +0500 Subject: [PATCH 1/6] Task 1: exit menu button, yes/no confirmation for exit, russian buttons text --- robots/src/gui/GameVisualizer.java | 168 ++++++++++------------- robots/src/gui/GameWindow.java | 10 +- robots/src/gui/LogWindow.java | 23 ++-- robots/src/gui/MainApplicationFrame.java | 157 ++++++++++----------- robots/src/gui/RobotsProgram.java | 28 ++-- robots/src/log/LogChangeListener.java | 2 +- 6 files changed, 177 insertions(+), 211 deletions(-) diff --git a/robots/src/gui/GameVisualizer.java b/robots/src/gui/GameVisualizer.java index f82cfd8f8..772c1bff8 100644 --- a/robots/src/gui/GameVisualizer.java +++ b/robots/src/gui/GameVisualizer.java @@ -13,49 +13,40 @@ import javax.swing.JPanel; -public class GameVisualizer extends JPanel -{ +public class GameVisualizer extends JPanel { private final Timer m_timer = initTimer(); - - private static Timer initTimer() - { + + private static Timer initTimer() { Timer timer = new Timer("events generator", true); return timer; } - + private volatile double m_robotPositionX = 100; - private volatile double m_robotPositionY = 100; - private volatile double m_robotDirection = 0; + private volatile double m_robotPositionY = 100; + private volatile double m_robotDirection = 0; private volatile int m_targetPositionX = 150; private volatile int m_targetPositionY = 100; - - private static final double maxVelocity = 0.1; - private static final double maxAngularVelocity = 0.001; - - public GameVisualizer() - { - m_timer.schedule(new TimerTask() - { + + private static final double maxVelocity = 0.1; + private static final double maxAngularVelocity = 0.001; + + public GameVisualizer() { + m_timer.schedule(new TimerTask() { @Override - public void run() - { + public void run() { onRedrawEvent(); } }, 0, 50); - m_timer.schedule(new TimerTask() - { + m_timer.schedule(new TimerTask() { @Override - public void run() - { + public void run() { onModelUpdateEvent(); } }, 0, 10); - addMouseListener(new MouseAdapter() - { + addMouseListener(new MouseAdapter() { @Override - public void mouseClicked(MouseEvent e) - { + public void mouseClicked(MouseEvent e) { setTargetPosition(e.getPoint()); repaint(); } @@ -63,144 +54,123 @@ public void mouseClicked(MouseEvent e) setDoubleBuffered(true); } - protected void setTargetPosition(Point p) - { + protected void setTargetPosition(Point p) { m_targetPositionX = p.x; m_targetPositionY = p.y; } - - protected void onRedrawEvent() - { + + protected void onRedrawEvent() { EventQueue.invokeLater(this::repaint); } - private static double distance(double x1, double y1, double x2, double y2) - { + private static double distance(double x1, double y1, double x2, double y2) { double diffX = x1 - x2; double diffY = y1 - y2; return Math.sqrt(diffX * diffX + diffY * diffY); } - - private static double angleTo(double fromX, double fromY, double toX, double toY) - { + + private static double angleTo(double fromX, double fromY, double toX, double toY) { double diffX = toX - fromX; double diffY = toY - fromY; - + return asNormalizedRadians(Math.atan2(diffY, diffX)); } - - protected void onModelUpdateEvent() - { - double distance = distance(m_targetPositionX, m_targetPositionY, - m_robotPositionX, m_robotPositionY); - if (distance < 0.5) - { + + protected void onModelUpdateEvent() { + double distance = distance(m_targetPositionX, m_targetPositionY, + m_robotPositionX, m_robotPositionY); + if (distance < 0.5) { return; } double velocity = maxVelocity; double angleToTarget = angleTo(m_robotPositionX, m_robotPositionY, m_targetPositionX, m_targetPositionY); double angularVelocity = 0; - if (angleToTarget > m_robotDirection) - { + if (angleToTarget > m_robotDirection) { angularVelocity = maxAngularVelocity; } - if (angleToTarget < m_robotDirection) - { + if (angleToTarget < m_robotDirection) { angularVelocity = -maxAngularVelocity; } - + moveRobot(velocity, angularVelocity, 10); } - - private static double applyLimits(double value, double min, double max) - { + + private static double applyLimits(double value, double min, double max) { if (value < min) return min; if (value > max) return max; return value; } - - private void moveRobot(double velocity, double angularVelocity, double duration) - { + + private void moveRobot(double velocity, double angularVelocity, double duration) { velocity = applyLimits(velocity, 0, maxVelocity); angularVelocity = applyLimits(angularVelocity, -maxAngularVelocity, maxAngularVelocity); - double newX = m_robotPositionX + velocity / angularVelocity * - (Math.sin(m_robotDirection + angularVelocity * duration) - - Math.sin(m_robotDirection)); - if (!Double.isFinite(newX)) - { + double newX = m_robotPositionX + velocity / angularVelocity * + (Math.sin(m_robotDirection + angularVelocity * duration) - + Math.sin(m_robotDirection)); + if (!Double.isFinite(newX)) { newX = m_robotPositionX + velocity * duration * Math.cos(m_robotDirection); } - double newY = m_robotPositionY - velocity / angularVelocity * - (Math.cos(m_robotDirection + angularVelocity * duration) - - Math.cos(m_robotDirection)); - if (!Double.isFinite(newY)) - { + double newY = m_robotPositionY - velocity / angularVelocity * + (Math.cos(m_robotDirection + angularVelocity * duration) - + Math.cos(m_robotDirection)); + if (!Double.isFinite(newY)) { newY = m_robotPositionY + velocity * duration * Math.sin(m_robotDirection); } m_robotPositionX = newX; m_robotPositionY = newY; - double newDirection = asNormalizedRadians(m_robotDirection + angularVelocity * duration); + double newDirection = asNormalizedRadians(m_robotDirection + angularVelocity * duration); m_robotDirection = newDirection; } - private static double asNormalizedRadians(double angle) - { - while (angle < 0) - { - angle += 2*Math.PI; + private static double asNormalizedRadians(double angle) { + while (angle < 0) { + angle += 2 * Math.PI; } - while (angle >= 2*Math.PI) - { - angle -= 2*Math.PI; + while (angle >= 2 * Math.PI) { + angle -= 2 * Math.PI; } return angle; } - - private static int round(double value) - { - return (int)(value + 0.5); + + private static int round(double value) { + return (int) (value + 0.5); } - + @Override - public void paint(Graphics g) - { + public void paint(Graphics g) { super.paint(g); - Graphics2D g2d = (Graphics2D)g; + Graphics2D g2d = (Graphics2D) g; drawRobot(g2d, round(m_robotPositionX), round(m_robotPositionY), m_robotDirection); drawTarget(g2d, m_targetPositionX, m_targetPositionY); } - - private static void fillOval(Graphics g, int centerX, int centerY, int diam1, int diam2) - { + + private static void fillOval(Graphics g, int centerX, int centerY, int diam1, int diam2) { g.fillOval(centerX - diam1 / 2, centerY - diam2 / 2, diam1, diam2); } - - private static void drawOval(Graphics g, int centerX, int centerY, int diam1, int diam2) - { + + private static void drawOval(Graphics g, int centerX, int centerY, int diam1, int diam2) { g.drawOval(centerX - diam1 / 2, centerY - diam2 / 2, diam1, diam2); } - - private void drawRobot(Graphics2D g, int x, int y, double direction) - { - int robotCenterX = round(m_robotPositionX); + + private void drawRobot(Graphics2D g, int x, int y, double direction) { + int robotCenterX = round(m_robotPositionX); int robotCenterY = round(m_robotPositionY); - AffineTransform t = AffineTransform.getRotateInstance(direction, robotCenterX, robotCenterY); + AffineTransform t = AffineTransform.getRotateInstance(direction, robotCenterX, robotCenterY); g.setTransform(t); g.setColor(Color.MAGENTA); fillOval(g, robotCenterX, robotCenterY, 30, 10); g.setColor(Color.BLACK); drawOval(g, robotCenterX, robotCenterY, 30, 10); g.setColor(Color.WHITE); - fillOval(g, robotCenterX + 10, robotCenterY, 5, 5); + fillOval(g, robotCenterX + 10, robotCenterY, 5, 5); g.setColor(Color.BLACK); - drawOval(g, robotCenterX + 10, robotCenterY, 5, 5); + drawOval(g, robotCenterX + 10, robotCenterY, 5, 5); } - - private void drawTarget(Graphics2D g, int x, int y) - { - AffineTransform t = AffineTransform.getRotateInstance(0, 0, 0); + + private void drawTarget(Graphics2D g, int x, int y) { + AffineTransform t = AffineTransform.getRotateInstance(0, 0, 0); g.setTransform(t); g.setColor(Color.GREEN); fillOval(g, x, y, 5, 5); diff --git a/robots/src/gui/GameWindow.java b/robots/src/gui/GameWindow.java index ecb63c00f..acaee1530 100644 --- a/robots/src/gui/GameWindow.java +++ b/robots/src/gui/GameWindow.java @@ -5,13 +5,11 @@ import javax.swing.JInternalFrame; import javax.swing.JPanel; -public class GameWindow extends JInternalFrame -{ - private final GameVisualizer m_visualizer; - public GameWindow() - { +public class GameWindow extends JInternalFrame { + + public GameWindow() { super("Игровое поле", true, true, true, true); - m_visualizer = new GameVisualizer(); + GameVisualizer m_visualizer = new GameVisualizer(); JPanel panel = new JPanel(new BorderLayout()); panel.add(m_visualizer, BorderLayout.CENTER); getContentPane().add(panel); diff --git a/robots/src/gui/LogWindow.java b/robots/src/gui/LogWindow.java index 723d3e2fc..0929c641f 100644 --- a/robots/src/gui/LogWindow.java +++ b/robots/src/gui/LogWindow.java @@ -11,19 +11,17 @@ import log.LogEntry; import log.LogWindowSource; -public class LogWindow extends JInternalFrame implements LogChangeListener -{ - private LogWindowSource m_logSource; - private TextArea m_logContent; +public class LogWindow extends JInternalFrame implements LogChangeListener { + private final LogWindowSource m_logSource; + private final TextArea m_logContent; - public LogWindow(LogWindowSource logSource) - { + public LogWindow(LogWindowSource logSource) { super("Протокол работы", true, true, true, true); m_logSource = logSource; m_logSource.registerListener(this); m_logContent = new TextArea(""); m_logContent.setSize(200, 500); - + JPanel panel = new JPanel(new BorderLayout()); panel.add(m_logContent, BorderLayout.CENTER); getContentPane().add(panel); @@ -31,20 +29,17 @@ public LogWindow(LogWindowSource logSource) updateLogContent(); } - private void updateLogContent() - { + private void updateLogContent() { StringBuilder content = new StringBuilder(); - for (LogEntry entry : m_logSource.all()) - { + for (LogEntry entry : m_logSource.all()) { content.append(entry.getMessage()).append("\n"); } m_logContent.setText(content.toString()); m_logContent.invalidate(); } - + @Override - public void onLogChanged() - { + public void onLogChanged() { EventQueue.invokeLater(this::updateLogContent); } } diff --git a/robots/src/gui/MainApplicationFrame.java b/robots/src/gui/MainApplicationFrame.java index 62e943ee1..f827c4272 100644 --- a/robots/src/gui/MainApplicationFrame.java +++ b/robots/src/gui/MainApplicationFrame.java @@ -4,68 +4,51 @@ import java.awt.Toolkit; import java.awt.event.KeyEvent; -import javax.swing.JDesktopPane; -import javax.swing.JFrame; -import javax.swing.JInternalFrame; -import javax.swing.JMenu; -import javax.swing.JMenuBar; -import javax.swing.JMenuItem; -import javax.swing.SwingUtilities; -import javax.swing.UIManager; -import javax.swing.UnsupportedLookAndFeelException; +import javax.swing.*; import log.Logger; -/** - * Что требуется сделать: - * 1. Метод создания меню перегружен функционалом и трудно читается. - * Следует разделить его на серию более простых методов (или вообще выделить отдельный класс). - * - */ -public class MainApplicationFrame extends JFrame -{ +public class MainApplicationFrame extends JFrame { private final JDesktopPane desktopPane = new JDesktopPane(); - + public MainApplicationFrame() { //Make the big window be indented 50 pixels from each edge //of the screen. - int inset = 50; + int inset = 50; Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); setBounds(inset, inset, - screenSize.width - inset*2, - screenSize.height - inset*2); + screenSize.width - inset * 2, + screenSize.height - inset * 2); setContentPane(desktopPane); - - + + LogWindow logWindow = createLogWindow(); addWindow(logWindow); GameWindow gameWindow = new GameWindow(); - gameWindow.setSize(400, 400); + gameWindow.setSize(400, 400); addWindow(gameWindow); setJMenuBar(generateMenuBar()); setDefaultCloseOperation(EXIT_ON_CLOSE); } - - protected LogWindow createLogWindow() - { + + protected LogWindow createLogWindow() { LogWindow logWindow = new LogWindow(Logger.getDefaultLogSource()); - logWindow.setLocation(10,10); + logWindow.setLocation(10, 10); logWindow.setSize(300, 800); setMinimumSize(logWindow.getSize()); logWindow.pack(); Logger.debug("Протокол работает"); return logWindow; } - - protected void addWindow(JInternalFrame frame) - { + + protected void addWindow(JInternalFrame frame) { desktopPane.add(frame); frame.setVisible(true); } - + // protected JMenuBar createMenuBar() { // JMenuBar menuBar = new JMenuBar(); // @@ -94,62 +77,82 @@ protected void addWindow(JInternalFrame frame) // // return menuBar; // } - - private JMenuBar generateMenuBar() - { + + private JMenuBar generateMenuBar() { JMenuBar menuBar = new JMenuBar(); - + + menuBar.add(createLookAndFeelMenu()); + menuBar.add(createTestMenu()); + menuBar.add(createExitButton()); + + return menuBar; + } + + private JMenuItem createExitButton() { + JMenuItem exit = new JMenuItem("Выход"); + exit.addActionListener(event -> confirmExit()); + return exit; + } + + private void confirmExit() { + UIManager.put("OptionPane.yesButtonText", "Да"); + UIManager.put("OptionPane.noButtonText", "Нет"); + if (JOptionPane.showConfirmDialog(null, "Вы уверены?", "ВЫХОД", + JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) { + System.exit(0); + } + } + + private JMenu createTestMenu() { + JMenu testMenu = new JMenu("Тесты"); + + testMenu.setMnemonic(KeyEvent.VK_T); + testMenu.getAccessibleContext().setAccessibleDescription("Тестовые команды"); + + JMenuItem addLogMessageItem = new JMenuItem("Сообщение в лог", KeyEvent.VK_S); + addLogMessageItem.addActionListener(event -> Logger.debug("Новая строка")); + testMenu.add(addLogMessageItem); + + return testMenu; + } + + private JMenu createLookAndFeelMenu() { JMenu lookAndFeelMenu = new JMenu("Режим отображения"); + lookAndFeelMenu.setMnemonic(KeyEvent.VK_V); lookAndFeelMenu.getAccessibleContext().setAccessibleDescription( "Управление режимом отображения приложения"); - - { - JMenuItem systemLookAndFeel = new JMenuItem("Системная схема", KeyEvent.VK_S); - systemLookAndFeel.addActionListener((event) -> { - setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); - this.invalidate(); - }); - lookAndFeelMenu.add(systemLookAndFeel); - } - { - JMenuItem crossplatformLookAndFeel = new JMenuItem("Универсальная схема", KeyEvent.VK_S); - crossplatformLookAndFeel.addActionListener((event) -> { - setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName()); - this.invalidate(); - }); - lookAndFeelMenu.add(crossplatformLookAndFeel); - } + lookAndFeelMenu.add(createSystemLookAndFeelItem()); + lookAndFeelMenu.add(createCrossPlatformLookAndFeelItem()); - JMenu testMenu = new JMenu("Тесты"); - testMenu.setMnemonic(KeyEvent.VK_T); - testMenu.getAccessibleContext().setAccessibleDescription( - "Тестовые команды"); - - { - JMenuItem addLogMessageItem = new JMenuItem("Сообщение в лог", KeyEvent.VK_S); - addLogMessageItem.addActionListener((event) -> { - Logger.debug("Новая строка"); - }); - testMenu.add(addLogMessageItem); - } + return lookAndFeelMenu; + } - menuBar.add(lookAndFeelMenu); - menuBar.add(testMenu); - return menuBar; + private JMenuItem createCrossPlatformLookAndFeelItem() { + JMenuItem crossPlatformLookAndFeel = new JMenuItem("Универсальная схема", KeyEvent.VK_S); + crossPlatformLookAndFeel.addActionListener(event -> { + setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName()); + this.invalidate(); + }); + return crossPlatformLookAndFeel; } - - private void setLookAndFeel(String className) - { - try - { + + private JMenuItem createSystemLookAndFeelItem() { + JMenuItem systemLookAndFeel = new JMenuItem("Системная схема", KeyEvent.VK_S); + systemLookAndFeel.addActionListener(event -> { + setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + this.invalidate(); + }); + return systemLookAndFeel; + } + + private void setLookAndFeel(String className) { + try { UIManager.setLookAndFeel(className); SwingUtilities.updateComponentTreeUI(this); - } - catch (ClassNotFoundException | InstantiationException - | IllegalAccessException | UnsupportedLookAndFeelException e) - { + } catch (ClassNotFoundException | InstantiationException + | IllegalAccessException | UnsupportedLookAndFeelException e) { // just ignore } } diff --git a/robots/src/gui/RobotsProgram.java b/robots/src/gui/RobotsProgram.java index ae0930a8b..f20e6b96a 100644 --- a/robots/src/gui/RobotsProgram.java +++ b/robots/src/gui/RobotsProgram.java @@ -5,21 +5,21 @@ import javax.swing.SwingUtilities; import javax.swing.UIManager; -public class RobotsProgram -{ +public class RobotsProgram { public static void main(String[] args) { - try { - UIManager.setLookAndFeel("javax.swing.plaf.nimbus.NimbusLookAndFeel"); + try { + UIManager.setLookAndFeel("javax.swing.plaf.nimbus.NimbusLookAndFeel"); // UIManager.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel"); // UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); // UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName()); - } catch (Exception e) { - e.printStackTrace(); - } - SwingUtilities.invokeLater(() -> { - MainApplicationFrame frame = new MainApplicationFrame(); - frame.pack(); - frame.setVisible(true); - frame.setExtendedState(Frame.MAXIMIZED_BOTH); - }); - }} + } catch (Exception e) { + e.printStackTrace(); + } + SwingUtilities.invokeLater(() -> { + MainApplicationFrame frame = new MainApplicationFrame(); + frame.pack(); + frame.setVisible(true); + frame.setExtendedState(Frame.MAXIMIZED_BOTH); + }); + } +} diff --git a/robots/src/log/LogChangeListener.java b/robots/src/log/LogChangeListener.java index 0b0fb85dd..2ba48db75 100644 --- a/robots/src/log/LogChangeListener.java +++ b/robots/src/log/LogChangeListener.java @@ -2,5 +2,5 @@ public interface LogChangeListener { - public void onLogChanged(); + void onLogChanged(); } From 18fb6ee1fc9d952f433f4cf38b6970fbebb9fc6e Mon Sep 17 00:00:00 2001 From: crystaline Date: Thu, 20 Apr 2023 15:42:54 +0500 Subject: [PATCH 2/6] add robot class and extract constants --- robots/src/gui/Constants.java | 47 +++++++++++ robots/src/gui/GameVisualizer.java | 72 +++++++--------- robots/src/gui/GameWindow.java | 4 +- robots/src/gui/LogWindow.java | 24 +++--- robots/src/gui/MainApplicationFrame.java | 100 ++++++++--------------- robots/src/gui/Robot.java | 38 +++++++++ robots/src/gui/RobotsProgram.java | 7 +- 7 files changed, 169 insertions(+), 123 deletions(-) create mode 100644 robots/src/gui/Constants.java create mode 100644 robots/src/gui/Robot.java diff --git a/robots/src/gui/Constants.java b/robots/src/gui/Constants.java new file mode 100644 index 000000000..170b96e37 --- /dev/null +++ b/robots/src/gui/Constants.java @@ -0,0 +1,47 @@ +package gui; + +public interface Constants { + + class MainApplicationFrameConstants { + + public static final int SCREEN_OFFSET = 50; + public static final int INITIAL_GAME_WINDOW_HEIGHT = 400; + public static final int INITIAL_GAME_WINDOW_WIDTH = 400; + public static final int LOG_WINDOW_INITIAL_LOCATION_X = 10; + public static final int LOG_WINDOW_INITIAL_LOCATION_Y = 10; + public static final int LOG_WINDOW_INITIAL_WIDTH = 300; + public static final int LOG_WINDOW_INITIAL_HEIGHT = 800; + public static final String LOGGER_INITIAL_MESSAGE = "Протокол работает"; + public static final String EXIT_BUTTON_TEXT = "Выход"; + public static final String YES_BUTTON_TEXT_CONSTANT = "OptionPane.yesButtonText"; + public static final String YES_BUTTON_TEXT = "Да"; + public static final String NO_BUTTON_TEXT_CONSTANT = "OptionPane.noButtonText"; + public static final String NO_BUTTON_TEXT = "Нет"; + public static final String EXIT_CONFIRM_DIALOG_TEXT = "Вы уверены?"; + public static final String EXIT_CONFIRM_DIALOG_TITLE = "ВЫХОД"; + public static final String TEST_MENU_TEXT = "Тесты"; + public static final String TEST_LOG_OPTION_TEXT = "Сообщение в лог"; + public static final String TEST_LOG_TEXT = "Новая строка"; + public static final String STYLE_MENU_TEXT = "Режим отображения"; + public static final String CROSS_PLATFORM_STYLE_TEXT = "Универсальная схема"; + public static final String SYSTEM_STYLE_TEXT = "Системная схема"; + } + + class RobotsProgramConstants { + + public static final String NIMBUS_MENU_STYLE = "javax.swing.plaf.nimbus.NimbusLookAndFeel"; + public static final String METAL_MENU_STYLE = "javax.swing.plaf.metal.MetalLookAndFeel"; + } + + class LogWindowConstants { + + public static final String INITIAL_LOG_MESSAGE = "Протокол работы"; + public static final int LOG_TEXT_AREA_WIDTH = 200; + public static final int LOG_TEXT_AREA_HEIGHT = 500; + } + + class GameWindowConstants { + + public static final String GAME_WINDOW_TITLE = "Игровое поле"; + } +} diff --git a/robots/src/gui/GameVisualizer.java b/robots/src/gui/GameVisualizer.java index 772c1bff8..9daef14ff 100644 --- a/robots/src/gui/GameVisualizer.java +++ b/robots/src/gui/GameVisualizer.java @@ -14,31 +14,22 @@ import javax.swing.JPanel; public class GameVisualizer extends JPanel { - private final Timer m_timer = initTimer(); - - private static Timer initTimer() { - Timer timer = new Timer("events generator", true); - return timer; - } - - private volatile double m_robotPositionX = 100; - private volatile double m_robotPositionY = 100; - private volatile double m_robotDirection = 0; - - private volatile int m_targetPositionX = 150; - private volatile int m_targetPositionY = 100; - + private final Timer timer = new Timer("events generator", true); + private volatile Robot robot; + private volatile int targetPositionX = 150; + private volatile int targetPositionY = 100; private static final double maxVelocity = 0.1; private static final double maxAngularVelocity = 0.001; public GameVisualizer() { - m_timer.schedule(new TimerTask() { + robot = new Robot(100, 100, 0); + timer.schedule(new TimerTask() { @Override public void run() { onRedrawEvent(); } }, 0, 50); - m_timer.schedule(new TimerTask() { + timer.schedule(new TimerTask() { @Override public void run() { onModelUpdateEvent(); @@ -55,8 +46,8 @@ public void mouseClicked(MouseEvent e) { } protected void setTargetPosition(Point p) { - m_targetPositionX = p.x; - m_targetPositionY = p.y; + targetPositionX = p.x; + targetPositionY = p.y; } protected void onRedrawEvent() { @@ -77,18 +68,18 @@ private static double angleTo(double fromX, double fromY, double toX, double toY } protected void onModelUpdateEvent() { - double distance = distance(m_targetPositionX, m_targetPositionY, - m_robotPositionX, m_robotPositionY); + double distance = distance(targetPositionX, targetPositionY, + robot.getXCoordinate(), robot.getYCoordinate()); if (distance < 0.5) { return; } double velocity = maxVelocity; - double angleToTarget = angleTo(m_robotPositionX, m_robotPositionY, m_targetPositionX, m_targetPositionY); + double angleToTarget = angleTo(robot.getXCoordinate(), robot.getYCoordinate(), targetPositionX, targetPositionY); double angularVelocity = 0; - if (angleToTarget > m_robotDirection) { + if (angleToTarget > robot.getDirection()) { angularVelocity = maxAngularVelocity; } - if (angleToTarget < m_robotDirection) { + if (angleToTarget < robot.getDirection()) { angularVelocity = -maxAngularVelocity; } @@ -98,30 +89,27 @@ protected void onModelUpdateEvent() { private static double applyLimits(double value, double min, double max) { if (value < min) return min; - if (value > max) - return max; - return value; + return Math.min(value, max); } private void moveRobot(double velocity, double angularVelocity, double duration) { velocity = applyLimits(velocity, 0, maxVelocity); angularVelocity = applyLimits(angularVelocity, -maxAngularVelocity, maxAngularVelocity); - double newX = m_robotPositionX + velocity / angularVelocity * - (Math.sin(m_robotDirection + angularVelocity * duration) - - Math.sin(m_robotDirection)); + double newX = robot.getXCoordinate() + velocity / angularVelocity * + (Math.sin(robot.getDirection() + angularVelocity * duration) - + Math.sin(robot.getDirection())); if (!Double.isFinite(newX)) { - newX = m_robotPositionX + velocity * duration * Math.cos(m_robotDirection); + newX = robot.getXCoordinate() + velocity * duration * Math.cos(robot.getDirection()); } - double newY = m_robotPositionY - velocity / angularVelocity * - (Math.cos(m_robotDirection + angularVelocity * duration) - - Math.cos(m_robotDirection)); + double newY = robot.getYCoordinate() - velocity / angularVelocity * + (Math.cos(robot.getDirection() + angularVelocity * duration) - + Math.cos(robot.getDirection())); if (!Double.isFinite(newY)) { - newY = m_robotPositionY + velocity * duration * Math.sin(m_robotDirection); + newY = robot.getYCoordinate() + velocity * duration * Math.sin(robot.getDirection()); } - m_robotPositionX = newX; - m_robotPositionY = newY; - double newDirection = asNormalizedRadians(m_robotDirection + angularVelocity * duration); - m_robotDirection = newDirection; + robot.setXCoordinate(newX); + robot.setYCoordinate(newY); + robot.setDirection(asNormalizedRadians(robot.getDirection() + angularVelocity * duration)); } private static double asNormalizedRadians(double angle) { @@ -142,8 +130,8 @@ private static int round(double value) { public void paint(Graphics g) { super.paint(g); Graphics2D g2d = (Graphics2D) g; - drawRobot(g2d, round(m_robotPositionX), round(m_robotPositionY), m_robotDirection); - drawTarget(g2d, m_targetPositionX, m_targetPositionY); + drawRobot(g2d, round(robot.getXCoordinate()), round(robot.getYCoordinate()), robot.getDirection()); + drawTarget(g2d, targetPositionX, targetPositionY); } private static void fillOval(Graphics g, int centerX, int centerY, int diam1, int diam2) { @@ -155,8 +143,8 @@ private static void drawOval(Graphics g, int centerX, int centerY, int diam1, in } private void drawRobot(Graphics2D g, int x, int y, double direction) { - int robotCenterX = round(m_robotPositionX); - int robotCenterY = round(m_robotPositionY); + int robotCenterX = round(robot.getXCoordinate()); + int robotCenterY = round(robot.getYCoordinate()); AffineTransform t = AffineTransform.getRotateInstance(direction, robotCenterX, robotCenterY); g.setTransform(t); g.setColor(Color.MAGENTA); diff --git a/robots/src/gui/GameWindow.java b/robots/src/gui/GameWindow.java index acaee1530..746404f27 100644 --- a/robots/src/gui/GameWindow.java +++ b/robots/src/gui/GameWindow.java @@ -5,10 +5,12 @@ import javax.swing.JInternalFrame; import javax.swing.JPanel; +import static gui.Constants.GameWindowConstants.GAME_WINDOW_TITLE; + public class GameWindow extends JInternalFrame { public GameWindow() { - super("Игровое поле", true, true, true, true); + super(GAME_WINDOW_TITLE, true, true, true, true); GameVisualizer m_visualizer = new GameVisualizer(); JPanel panel = new JPanel(new BorderLayout()); panel.add(m_visualizer, BorderLayout.CENTER); diff --git a/robots/src/gui/LogWindow.java b/robots/src/gui/LogWindow.java index 0929c641f..e382750e1 100644 --- a/robots/src/gui/LogWindow.java +++ b/robots/src/gui/LogWindow.java @@ -11,19 +11,21 @@ import log.LogEntry; import log.LogWindowSource; +import static gui.Constants.LogWindowConstants.*; + public class LogWindow extends JInternalFrame implements LogChangeListener { - private final LogWindowSource m_logSource; - private final TextArea m_logContent; + private final LogWindowSource logSource; + private final TextArea logContent; public LogWindow(LogWindowSource logSource) { - super("Протокол работы", true, true, true, true); - m_logSource = logSource; - m_logSource.registerListener(this); - m_logContent = new TextArea(""); - m_logContent.setSize(200, 500); + super(INITIAL_LOG_MESSAGE, true, true, true, true); + this.logSource = logSource; + this.logSource.registerListener(this); + logContent = new TextArea(""); + logContent.setSize(LOG_TEXT_AREA_WIDTH, LOG_TEXT_AREA_HEIGHT); JPanel panel = new JPanel(new BorderLayout()); - panel.add(m_logContent, BorderLayout.CENTER); + panel.add(logContent, BorderLayout.CENTER); getContentPane().add(panel); pack(); updateLogContent(); @@ -31,11 +33,11 @@ public LogWindow(LogWindowSource logSource) { private void updateLogContent() { StringBuilder content = new StringBuilder(); - for (LogEntry entry : m_logSource.all()) { + for (LogEntry entry : logSource.all()) { content.append(entry.getMessage()).append("\n"); } - m_logContent.setText(content.toString()); - m_logContent.invalidate(); + logContent.setText(content.toString()); + logContent.invalidate(); } @Override diff --git a/robots/src/gui/MainApplicationFrame.java b/robots/src/gui/MainApplicationFrame.java index f827c4272..fb99aa79d 100644 --- a/robots/src/gui/MainApplicationFrame.java +++ b/robots/src/gui/MainApplicationFrame.java @@ -8,17 +8,16 @@ import log.Logger; +import static gui.Constants.MainApplicationFrameConstants.*; + public class MainApplicationFrame extends JFrame { private final JDesktopPane desktopPane = new JDesktopPane(); public MainApplicationFrame() { - //Make the big window be indented 50 pixels from each edge - //of the screen. - int inset = 50; Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); - setBounds(inset, inset, - screenSize.width - inset * 2, - screenSize.height - inset * 2); + setBounds(SCREEN_OFFSET, SCREEN_OFFSET, + screenSize.width - SCREEN_OFFSET * 2, + screenSize.height - SCREEN_OFFSET * 2); setContentPane(desktopPane); @@ -27,7 +26,7 @@ public MainApplicationFrame() { addWindow(logWindow); GameWindow gameWindow = new GameWindow(); - gameWindow.setSize(400, 400); + gameWindow.setSize(INITIAL_GAME_WINDOW_WIDTH, INITIAL_GAME_WINDOW_HEIGHT); addWindow(gameWindow); setJMenuBar(generateMenuBar()); @@ -36,11 +35,11 @@ public MainApplicationFrame() { protected LogWindow createLogWindow() { LogWindow logWindow = new LogWindow(Logger.getDefaultLogSource()); - logWindow.setLocation(10, 10); - logWindow.setSize(300, 800); + logWindow.setLocation(LOG_WINDOW_INITIAL_LOCATION_X, LOG_WINDOW_INITIAL_LOCATION_Y); + logWindow.setSize(LOG_WINDOW_INITIAL_WIDTH, LOG_WINDOW_INITIAL_HEIGHT); setMinimumSize(logWindow.getSize()); logWindow.pack(); - Logger.debug("Протокол работает"); + Logger.debug(LOGGER_INITIAL_MESSAGE); return logWindow; } @@ -49,35 +48,6 @@ protected void addWindow(JInternalFrame frame) { frame.setVisible(true); } -// protected JMenuBar createMenuBar() { -// JMenuBar menuBar = new JMenuBar(); -// -// //Set up the lone menu. -// JMenu menu = new JMenu("Document"); -// menu.setMnemonic(KeyEvent.VK_D); -// menuBar.add(menu); -// -// //Set up the first menu item. -// JMenuItem menuItem = new JMenuItem("New"); -// menuItem.setMnemonic(KeyEvent.VK_N); -// menuItem.setAccelerator(KeyStroke.getKeyStroke( -// KeyEvent.VK_N, ActionEvent.ALT_MASK)); -// menuItem.setActionCommand("new"); -//// menuItem.addActionListener(this); -// menu.add(menuItem); -// -// //Set up the second menu item. -// menuItem = new JMenuItem("Quit"); -// menuItem.setMnemonic(KeyEvent.VK_Q); -// menuItem.setAccelerator(KeyStroke.getKeyStroke( -// KeyEvent.VK_Q, ActionEvent.ALT_MASK)); -// menuItem.setActionCommand("quit"); -//// menuItem.addActionListener(this); -// menu.add(menuItem); -// -// return menuBar; -// } - private JMenuBar generateMenuBar() { JMenuBar menuBar = new JMenuBar(); @@ -89,71 +59,67 @@ private JMenuBar generateMenuBar() { } private JMenuItem createExitButton() { - JMenuItem exit = new JMenuItem("Выход"); + JMenuItem exit = new JMenuItem(EXIT_BUTTON_TEXT); exit.addActionListener(event -> confirmExit()); return exit; } private void confirmExit() { - UIManager.put("OptionPane.yesButtonText", "Да"); - UIManager.put("OptionPane.noButtonText", "Нет"); - if (JOptionPane.showConfirmDialog(null, "Вы уверены?", "ВЫХОД", - JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) { + UIManager.put(YES_BUTTON_TEXT_CONSTANT, YES_BUTTON_TEXT); + UIManager.put(NO_BUTTON_TEXT_CONSTANT, NO_BUTTON_TEXT); + if (JOptionPane.showConfirmDialog(null, EXIT_CONFIRM_DIALOG_TEXT, EXIT_CONFIRM_DIALOG_TITLE, JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) { System.exit(0); } } private JMenu createTestMenu() { - JMenu testMenu = new JMenu("Тесты"); + JMenu testMenu = new JMenu(TEST_MENU_TEXT); testMenu.setMnemonic(KeyEvent.VK_T); - testMenu.getAccessibleContext().setAccessibleDescription("Тестовые команды"); - JMenuItem addLogMessageItem = new JMenuItem("Сообщение в лог", KeyEvent.VK_S); - addLogMessageItem.addActionListener(event -> Logger.debug("Новая строка")); + JMenuItem addLogMessageItem = new JMenuItem(TEST_LOG_OPTION_TEXT, KeyEvent.VK_S); + addLogMessageItem.addActionListener(event -> Logger.debug(TEST_LOG_TEXT)); testMenu.add(addLogMessageItem); return testMenu; } private JMenu createLookAndFeelMenu() { - JMenu lookAndFeelMenu = new JMenu("Режим отображения"); + JMenu styleMenu = new JMenu(STYLE_MENU_TEXT); - lookAndFeelMenu.setMnemonic(KeyEvent.VK_V); - lookAndFeelMenu.getAccessibleContext().setAccessibleDescription( - "Управление режимом отображения приложения"); + styleMenu.setMnemonic(KeyEvent.VK_V); - lookAndFeelMenu.add(createSystemLookAndFeelItem()); - lookAndFeelMenu.add(createCrossPlatformLookAndFeelItem()); + styleMenu.add(createSystemStyleItem()); + styleMenu.add(createCrossPlatformStyleItem()); - return lookAndFeelMenu; + return styleMenu; } - private JMenuItem createCrossPlatformLookAndFeelItem() { - JMenuItem crossPlatformLookAndFeel = new JMenuItem("Универсальная схема", KeyEvent.VK_S); - crossPlatformLookAndFeel.addActionListener(event -> { - setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName()); + private JMenuItem createCrossPlatformStyleItem() { + JMenuItem crossPlatformStyle = new JMenuItem(CROSS_PLATFORM_STYLE_TEXT, KeyEvent.VK_S); + crossPlatformStyle.addActionListener(event -> { + setStyle(UIManager.getCrossPlatformLookAndFeelClassName()); this.invalidate(); }); - return crossPlatformLookAndFeel; + return crossPlatformStyle; } - private JMenuItem createSystemLookAndFeelItem() { - JMenuItem systemLookAndFeel = new JMenuItem("Системная схема", KeyEvent.VK_S); - systemLookAndFeel.addActionListener(event -> { - setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + private JMenuItem createSystemStyleItem() { + JMenuItem systemStyle = new JMenuItem(SYSTEM_STYLE_TEXT, KeyEvent.VK_S); + systemStyle.addActionListener(event -> { + setStyle(UIManager.getSystemLookAndFeelClassName()); this.invalidate(); }); - return systemLookAndFeel; + return systemStyle; } - private void setLookAndFeel(String className) { + private void setStyle(String className) { try { UIManager.setLookAndFeel(className); SwingUtilities.updateComponentTreeUI(this); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException e) { - // just ignore + e.printStackTrace(); } } } diff --git a/robots/src/gui/Robot.java b/robots/src/gui/Robot.java new file mode 100644 index 000000000..7b09e747b --- /dev/null +++ b/robots/src/gui/Robot.java @@ -0,0 +1,38 @@ +package gui; + +public class Robot { + + private volatile double XCoordinate; + private volatile double YCoordinate; + private volatile double Direction; + + public void setXCoordinate(double XCoordinate) { + this.XCoordinate = XCoordinate; + } + + public void setYCoordinate(double YCoordinate) { + this.YCoordinate = YCoordinate; + } + + public void setDirection(double direction) { + Direction = direction; + } + + public double getXCoordinate() { + return XCoordinate; + } + + public double getYCoordinate() { + return YCoordinate; + } + + public double getDirection() { + return Direction; + } + + public Robot(double XCoordinate, double YCoordinate, double direction) { + this.XCoordinate = XCoordinate; + this.YCoordinate = YCoordinate; + Direction = direction; + } +} diff --git a/robots/src/gui/RobotsProgram.java b/robots/src/gui/RobotsProgram.java index f20e6b96a..9c45fab05 100644 --- a/robots/src/gui/RobotsProgram.java +++ b/robots/src/gui/RobotsProgram.java @@ -5,11 +5,14 @@ import javax.swing.SwingUtilities; import javax.swing.UIManager; +import static gui.Constants.RobotsProgramConstants.METAL_MENU_STYLE; +import static gui.Constants.RobotsProgramConstants.NIMBUS_MENU_STYLE; + public class RobotsProgram { public static void main(String[] args) { try { - UIManager.setLookAndFeel("javax.swing.plaf.nimbus.NimbusLookAndFeel"); -// UIManager.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel"); + UIManager.setLookAndFeel(NIMBUS_MENU_STYLE); +// UIManager.setLookAndFeel(METAL_MENU_STYLE); // UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); // UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName()); } catch (Exception e) { From 144dc68e7894a5e62c24f1b6e81501326c9ff925 Mon Sep 17 00:00:00 2001 From: crystaline Date: Thu, 27 Apr 2023 17:09:10 +0500 Subject: [PATCH 3/6] add robot and target classes and refactor GameVisualizer class --- robots/src/gui/Constants.java | 28 ++++++ robots/src/gui/GameVisualizer.java | 130 +++++++++++++--------------- robots/src/gui/Robot.java | 30 +++---- robots/src/gui/Target.java | 28 ++++++ robots/src/log/LogEntry.java | 15 ++-- robots/src/log/LogLevel.java | 14 +-- robots/src/log/LogWindowSource.java | 103 ++++++++++------------ robots/src/log/Logger.java | 7 +- 8 files changed, 189 insertions(+), 166 deletions(-) create mode 100644 robots/src/gui/Target.java diff --git a/robots/src/gui/Constants.java b/robots/src/gui/Constants.java index 170b96e37..fa6a71b80 100644 --- a/robots/src/gui/Constants.java +++ b/robots/src/gui/Constants.java @@ -44,4 +44,32 @@ class GameWindowConstants { public static final String GAME_WINDOW_TITLE = "Игровое поле"; } + + class GameVisualizerConstants { + + public static final double ROBOT_VELOCITY = 0.1; + public static final double MAX_ANGULAR_VELOCITY = 0.001; + public static final double ROBOT_STOP_DISTANCE = 0.5; + public static final String TIMER_NAME = "events generator"; + public static final int TIMER_DELAY = 0; + public static final int TIMER_REDRAW_PERIOD = 50; + public static final int TIMER_UPDATE_PERIOD = 10; + public static final int ROBOT_INITIAL_X_COORDINATE = 100; + public static final int ROBOT_INITIAL_Y_COORDINATE = 100; + public static final int ROBOT_INITIAL_DIRECTION = 0; + public static final int ROBOT_BODY_FIRST_DIAMETER = 30; + public static final int ROBOT_BODY_SECOND_DIAMETER = 10; + public static final int ROBOT_HEAD_X_OFFSET = 10; + public static final int ROBOT_HEAD_DIAMETER = 5; + public static final int TARGET_DIAMETER = 5; + public static final int TARGET_THETA = 0; + public static final int TARGET_ANCHORX = 0; + public static final int TARGET_ANCHORY = 0; + public static final int TARGET_INITIAL_X_COORDINATE = 150; + public static final int TARGET_INITIAL_Y_COORDINATE = 100; + } + + class LoggerConstants { + public static final int LOGGER_IQUEUE_LENGTH = 100; + } } diff --git a/robots/src/gui/GameVisualizer.java b/robots/src/gui/GameVisualizer.java index 9daef14ff..3ce1acb67 100644 --- a/robots/src/gui/GameVisualizer.java +++ b/robots/src/gui/GameVisualizer.java @@ -13,106 +13,94 @@ import javax.swing.JPanel; +import static gui.Constants.GameVisualizerConstants.*; + public class GameVisualizer extends JPanel { - private final Timer timer = new Timer("events generator", true); - private volatile Robot robot; - private volatile int targetPositionX = 150; - private volatile int targetPositionY = 100; - private static final double maxVelocity = 0.1; - private static final double maxAngularVelocity = 0.001; + private final Robot robot; + private final Target target; public GameVisualizer() { - robot = new Robot(100, 100, 0); + robot = new Robot(ROBOT_INITIAL_X_COORDINATE, ROBOT_INITIAL_Y_COORDINATE, ROBOT_INITIAL_DIRECTION); + target = new Target(TARGET_INITIAL_X_COORDINATE, TARGET_INITIAL_Y_COORDINATE); + Timer timer = new Timer(TIMER_NAME, true); timer.schedule(new TimerTask() { @Override public void run() { onRedrawEvent(); } - }, 0, 50); + }, TIMER_DELAY, TIMER_REDRAW_PERIOD); timer.schedule(new TimerTask() { @Override public void run() { onModelUpdateEvent(); } - }, 0, 10); + }, TIMER_DELAY, TIMER_UPDATE_PERIOD); addMouseListener(new MouseAdapter() { @Override - public void mouseClicked(MouseEvent e) { - setTargetPosition(e.getPoint()); + public void mouseClicked(MouseEvent event) { + setTargetPosition(event.getPoint()); repaint(); } }); setDoubleBuffered(true); } - protected void setTargetPosition(Point p) { - targetPositionX = p.x; - targetPositionY = p.y; + private void setTargetPosition(Point point) { + target.setXCoordinate(point.x); + target.setYCoordinate(point.y); } - protected void onRedrawEvent() { + private void onRedrawEvent() { EventQueue.invokeLater(this::repaint); } - private static double distance(double x1, double y1, double x2, double y2) { - double diffX = x1 - x2; - double diffY = y1 - y2; - return Math.sqrt(diffX * diffX + diffY * diffY); + private double calculateDistance(double firstPointXCoordinate, double firstPointYCoordinate, double secondPointXCoordinate, double secondPointYCoordinate) { + double xCoordinateDifference = firstPointXCoordinate - secondPointXCoordinate; + double yCoordinateDifference = firstPointYCoordinate - secondPointYCoordinate; + return Math.sqrt(xCoordinateDifference * xCoordinateDifference + yCoordinateDifference * yCoordinateDifference); } - private static double angleTo(double fromX, double fromY, double toX, double toY) { - double diffX = toX - fromX; - double diffY = toY - fromY; - - return asNormalizedRadians(Math.atan2(diffY, diffX)); + private double calculateAngle(double startXCoordinate, double startYCoordinate, double endXCoordinate, double endYCoordinate) { + double xCoordinateDifference = endXCoordinate - startXCoordinate; + double yCoordinateDifference = endYCoordinate - startYCoordinate; + return asNormalizedRadians(Math.atan2(yCoordinateDifference, xCoordinateDifference)); } - protected void onModelUpdateEvent() { - double distance = distance(targetPositionX, targetPositionY, - robot.getXCoordinate(), robot.getYCoordinate()); - if (distance < 0.5) { + private void onModelUpdateEvent() { + double distance = calculateDistance(target.getXCoordinate(), target.getYCoordinate(), robot.getXCoordinate(), robot.getYCoordinate()); + if (distance < ROBOT_STOP_DISTANCE) { return; } - double velocity = maxVelocity; - double angleToTarget = angleTo(robot.getXCoordinate(), robot.getYCoordinate(), targetPositionX, targetPositionY); + double angleToTarget = calculateAngle(robot.getXCoordinate(), robot.getYCoordinate(), target.getXCoordinate(), target.getYCoordinate()); double angularVelocity = 0; if (angleToTarget > robot.getDirection()) { - angularVelocity = maxAngularVelocity; + angularVelocity = MAX_ANGULAR_VELOCITY; } if (angleToTarget < robot.getDirection()) { - angularVelocity = -maxAngularVelocity; + angularVelocity = -MAX_ANGULAR_VELOCITY; } - moveRobot(velocity, angularVelocity, 10); - } - - private static double applyLimits(double value, double min, double max) { - if (value < min) - return min; - return Math.min(value, max); + moveRobot(angularVelocity); } - private void moveRobot(double velocity, double angularVelocity, double duration) { - velocity = applyLimits(velocity, 0, maxVelocity); - angularVelocity = applyLimits(angularVelocity, -maxAngularVelocity, maxAngularVelocity); - double newX = robot.getXCoordinate() + velocity / angularVelocity * - (Math.sin(robot.getDirection() + angularVelocity * duration) - - Math.sin(robot.getDirection())); - if (!Double.isFinite(newX)) { - newX = robot.getXCoordinate() + velocity * duration * Math.cos(robot.getDirection()); + private void moveRobot(double angularVelocity) { + double newRobotDirection = robot.getDirection() + angularVelocity * TIMER_UPDATE_PERIOD; + double newRobotXCoordinate = robot.getXCoordinate() + ROBOT_VELOCITY / angularVelocity * + (Math.sin(newRobotDirection) - Math.sin(robot.getDirection())); + if (!Double.isFinite(newRobotXCoordinate)) { + newRobotXCoordinate = robot.getXCoordinate() + ROBOT_VELOCITY * TIMER_UPDATE_PERIOD * Math.cos(robot.getDirection()); } - double newY = robot.getYCoordinate() - velocity / angularVelocity * - (Math.cos(robot.getDirection() + angularVelocity * duration) - - Math.cos(robot.getDirection())); - if (!Double.isFinite(newY)) { - newY = robot.getYCoordinate() + velocity * duration * Math.sin(robot.getDirection()); + double newRobotYCoordinate = robot.getYCoordinate() - ROBOT_VELOCITY / angularVelocity * + (Math.cos(newRobotDirection) - Math.cos(robot.getDirection())); + if (!Double.isFinite(newRobotYCoordinate)) { + newRobotYCoordinate = robot.getYCoordinate() + ROBOT_VELOCITY * TIMER_UPDATE_PERIOD * Math.sin(robot.getDirection()); } - robot.setXCoordinate(newX); - robot.setYCoordinate(newY); - robot.setDirection(asNormalizedRadians(robot.getDirection() + angularVelocity * duration)); + robot.setXCoordinate(newRobotXCoordinate); + robot.setYCoordinate(newRobotYCoordinate); + robot.setDirection(asNormalizedRadians(newRobotDirection)); } - private static double asNormalizedRadians(double angle) { + private double asNormalizedRadians(double angle) { while (angle < 0) { angle += 2 * Math.PI; } @@ -122,7 +110,7 @@ private static double asNormalizedRadians(double angle) { return angle; } - private static int round(double value) { + private int round(double value) { return (int) (value + 0.5); } @@ -130,39 +118,39 @@ private static int round(double value) { public void paint(Graphics g) { super.paint(g); Graphics2D g2d = (Graphics2D) g; - drawRobot(g2d, round(robot.getXCoordinate()), round(robot.getYCoordinate()), robot.getDirection()); - drawTarget(g2d, targetPositionX, targetPositionY); + drawRobot(g2d, robot.getDirection()); + drawTarget(g2d, target.getXCoordinate(), target.getYCoordinate()); } - private static void fillOval(Graphics g, int centerX, int centerY, int diam1, int diam2) { - g.fillOval(centerX - diam1 / 2, centerY - diam2 / 2, diam1, diam2); + private void fillOval(Graphics g, int centerXCoordinate, int centerYCoordinate, int firstDiameter, int secondDiameter) { + g.fillOval(centerXCoordinate - firstDiameter / 2, centerYCoordinate - secondDiameter / 2, firstDiameter, secondDiameter); } - private static void drawOval(Graphics g, int centerX, int centerY, int diam1, int diam2) { - g.drawOval(centerX - diam1 / 2, centerY - diam2 / 2, diam1, diam2); + private void drawOval(Graphics g, int centerXCoordinate, int centerYCoordinate, int firstDiameter, int secondDiameter) { + g.drawOval(centerXCoordinate - firstDiameter / 2, centerYCoordinate - secondDiameter / 2, firstDiameter, secondDiameter); } - private void drawRobot(Graphics2D g, int x, int y, double direction) { + private void drawRobot(Graphics2D g, double direction) { int robotCenterX = round(robot.getXCoordinate()); int robotCenterY = round(robot.getYCoordinate()); AffineTransform t = AffineTransform.getRotateInstance(direction, robotCenterX, robotCenterY); g.setTransform(t); g.setColor(Color.MAGENTA); - fillOval(g, robotCenterX, robotCenterY, 30, 10); + fillOval(g, robotCenterX, robotCenterY, ROBOT_BODY_FIRST_DIAMETER, ROBOT_BODY_SECOND_DIAMETER); g.setColor(Color.BLACK); - drawOval(g, robotCenterX, robotCenterY, 30, 10); + drawOval(g, robotCenterX, robotCenterY, ROBOT_BODY_FIRST_DIAMETER, ROBOT_BODY_SECOND_DIAMETER); g.setColor(Color.WHITE); - fillOval(g, robotCenterX + 10, robotCenterY, 5, 5); + fillOval(g, robotCenterX + ROBOT_HEAD_X_OFFSET, robotCenterY, ROBOT_HEAD_DIAMETER, ROBOT_HEAD_DIAMETER); g.setColor(Color.BLACK); - drawOval(g, robotCenterX + 10, robotCenterY, 5, 5); + drawOval(g, robotCenterX + ROBOT_HEAD_X_OFFSET, robotCenterY, ROBOT_HEAD_DIAMETER, ROBOT_HEAD_DIAMETER); } private void drawTarget(Graphics2D g, int x, int y) { - AffineTransform t = AffineTransform.getRotateInstance(0, 0, 0); + AffineTransform t = AffineTransform.getRotateInstance(TARGET_THETA, TARGET_ANCHORX, TARGET_ANCHORY); g.setTransform(t); g.setColor(Color.GREEN); - fillOval(g, x, y, 5, 5); + fillOval(g, x, y, TARGET_DIAMETER, TARGET_DIAMETER); g.setColor(Color.BLACK); - drawOval(g, x, y, 5, 5); + drawOval(g, x, y, TARGET_DIAMETER, TARGET_DIAMETER); } } diff --git a/robots/src/gui/Robot.java b/robots/src/gui/Robot.java index 7b09e747b..a0deaf678 100644 --- a/robots/src/gui/Robot.java +++ b/robots/src/gui/Robot.java @@ -2,37 +2,37 @@ public class Robot { - private volatile double XCoordinate; - private volatile double YCoordinate; - private volatile double Direction; + private volatile double xCoordinate; + private volatile double yCoordinate; + private volatile double direction; - public void setXCoordinate(double XCoordinate) { - this.XCoordinate = XCoordinate; + public void setXCoordinate(double xCoordinate) { + this.xCoordinate = xCoordinate; } - public void setYCoordinate(double YCoordinate) { - this.YCoordinate = YCoordinate; + public void setYCoordinate(double yCoordinate) { + this.yCoordinate = yCoordinate; } public void setDirection(double direction) { - Direction = direction; + this.direction = direction; } public double getXCoordinate() { - return XCoordinate; + return xCoordinate; } public double getYCoordinate() { - return YCoordinate; + return yCoordinate; } public double getDirection() { - return Direction; + return direction; } - public Robot(double XCoordinate, double YCoordinate, double direction) { - this.XCoordinate = XCoordinate; - this.YCoordinate = YCoordinate; - Direction = direction; + public Robot(double xCoordinate, double yCoordinate, double direction) { + this.xCoordinate = xCoordinate; + this.yCoordinate = yCoordinate; + this.direction = direction; } } diff --git a/robots/src/gui/Target.java b/robots/src/gui/Target.java new file mode 100644 index 000000000..46e9cd6a8 --- /dev/null +++ b/robots/src/gui/Target.java @@ -0,0 +1,28 @@ +package gui; + +public class Target { + + private volatile int xCoordinate; + private volatile int yCoordinate; + + public int getXCoordinate() { + return xCoordinate; + } + + public void setXCoordinate(int xCoordinate) { + this.xCoordinate = xCoordinate; + } + + public int getYCoordinate() { + return yCoordinate; + } + + public void setYCoordinate(int yCoordinate) { + this.yCoordinate = yCoordinate; + } + + public Target(int xCoordinate, int yCoordinate) { + this.xCoordinate = xCoordinate; + this.yCoordinate = yCoordinate; + } +} diff --git a/robots/src/log/LogEntry.java b/robots/src/log/LogEntry.java index 3d9147107..a2b7e2c94 100644 --- a/robots/src/log/LogEntry.java +++ b/robots/src/log/LogEntry.java @@ -2,23 +2,18 @@ public class LogEntry { - private LogLevel m_logLevel; - private String m_strMessage; + private final LogLevel logLevel; + private final String strMessage; public LogEntry(LogLevel logLevel, String strMessage) { - m_strMessage = strMessage; - m_logLevel = logLevel; + this.strMessage = strMessage; + this.logLevel = logLevel; } public String getMessage() { - return m_strMessage; - } - - public LogLevel getLevel() - { - return m_logLevel; + return strMessage; } } diff --git a/robots/src/log/LogLevel.java b/robots/src/log/LogLevel.java index 582d010cc..ac4b012d0 100644 --- a/robots/src/log/LogLevel.java +++ b/robots/src/log/LogLevel.java @@ -8,17 +8,17 @@ public enum LogLevel Warning(3), Error(4), Fatal(5); - - private int m_iLevel; - - private LogLevel(int iLevel) + + private final int iLevel; + + LogLevel(int iLevel) { - m_iLevel = iLevel; + this.iLevel = iLevel; } - + public int level() { - return m_iLevel; + return iLevel; } } diff --git a/robots/src/log/LogWindowSource.java b/robots/src/log/LogWindowSource.java index ca0ce4426..e537fc259 100644 --- a/robots/src/log/LogWindowSource.java +++ b/robots/src/log/LogWindowSource.java @@ -7,83 +7,68 @@ * Что починить: * 1. Этот класс порождает утечку ресурсов (связанные слушатели оказываются * удерживаемыми в памяти) - * 2. Этот класс хранит активные сообщения лога, но в такой реализации он - * их лишь накапливает. Надо же, чтобы количество сообщений в логе было ограничено - * величиной m_iQueueLength (т.е. реально нужна очередь сообщений - * ограниченного размера) + * 2. Этот класс хранит активные сообщения лога, но в такой реализации он + * их лишь накапливает. Надо же, чтобы количество сообщений в логе было ограничено + * величиной m_iQueueLength (т.е. реально нужна очередь сообщений + * ограниченного размера) */ -public class LogWindowSource -{ - private int m_iQueueLength; - - private ArrayList m_messages; - private final ArrayList m_listeners; - private volatile LogChangeListener[] m_activeListeners; - - public LogWindowSource(int iQueueLength) - { - m_iQueueLength = iQueueLength; - m_messages = new ArrayList(iQueueLength); - m_listeners = new ArrayList(); +public class LogWindowSource { + private final int iQueueLength; + + private final ArrayList messages; + private final ArrayList listeners; + private volatile LogChangeListener[] activeListeners; + + public LogWindowSource(int iQueueLength) { + this.iQueueLength = iQueueLength; + messages = new ArrayList<>(iQueueLength); + listeners = new ArrayList<>(); } - - public void registerListener(LogChangeListener listener) - { - synchronized(m_listeners) - { - m_listeners.add(listener); - m_activeListeners = null; + + public void registerListener(LogChangeListener listener) { + synchronized (listeners) { + listeners.add(listener); + activeListeners = null; } } - - public void unregisterListener(LogChangeListener listener) - { - synchronized(m_listeners) - { - m_listeners.remove(listener); - m_activeListeners = null; + + public void unregisterListener(LogChangeListener listener) { + synchronized (listeners) { + listeners.remove(listener); + activeListeners = null; } } - - public void append(LogLevel logLevel, String strMessage) - { + + public void append(LogLevel logLevel, String strMessage) { LogEntry entry = new LogEntry(logLevel, strMessage); - m_messages.add(entry); - LogChangeListener [] activeListeners = m_activeListeners; - if (activeListeners == null) - { - synchronized (m_listeners) - { - if (m_activeListeners == null) - { - activeListeners = m_listeners.toArray(new LogChangeListener [0]); - m_activeListeners = activeListeners; + messages.add(entry); + LogChangeListener[] activeListeners = this.activeListeners; + if (activeListeners == null) { + synchronized (listeners) { + if (this.activeListeners == null) { + activeListeners = listeners.toArray(new LogChangeListener[0]); + this.activeListeners = activeListeners; } } } - for (LogChangeListener listener : activeListeners) - { + for (LogChangeListener listener : activeListeners) { listener.onLogChanged(); } } - - public int size() - { - return m_messages.size(); + + public int size() { + return messages.size(); } - public Iterable range(int startFrom, int count) - { - if (startFrom < 0 || startFrom >= m_messages.size()) - { + public Iterable range(int startFrom, int count) { + if (startFrom < 0 || startFrom >= messages.size()) { return Collections.emptyList(); } - int indexTo = Math.min(startFrom + count, m_messages.size()); - return m_messages.subList(startFrom, indexTo); + int indexTo = Math.min(startFrom + count, messages.size()); + return messages.subList(startFrom, indexTo); } - public Iterable all() - { - return m_messages; + public Iterable all() { + return messages; } } diff --git a/robots/src/log/Logger.java b/robots/src/log/Logger.java index b008a5d01..8ae200172 100644 --- a/robots/src/log/Logger.java +++ b/robots/src/log/Logger.java @@ -1,11 +1,10 @@ package log; +import static gui.Constants.LoggerConstants.LOGGER_IQUEUE_LENGTH; + public final class Logger { - private static final LogWindowSource defaultLogSource; - static { - defaultLogSource = new LogWindowSource(100); - } + private static final LogWindowSource defaultLogSource = new LogWindowSource(LOGGER_IQUEUE_LENGTH); private Logger() { From e865a8525f541088a66c11cfe45bbb0e81a40b26 Mon Sep 17 00:00:00 2001 From: crystaline Date: Mon, 15 May 2023 16:17:24 +0500 Subject: [PATCH 4/6] Use MVC model for project, separate functionality across the other classes, add movement for target. --- robots/src/gui/Constants.java | 4 + robots/src/gui/GameController.java | 75 ++++++++++++++ robots/src/gui/GameModel.java | 32 ++++++ robots/src/gui/GameVisualizer.java | 148 +-------------------------- robots/src/gui/GameWindow.java | 4 +- robots/src/gui/KeyEventListener.java | 49 +++++++++ robots/src/gui/MathModule.java | 30 ++++++ robots/src/gui/Moveable.java | 6 ++ robots/src/gui/Robot.java | 64 +++++++++--- robots/src/gui/Target.java | 55 +++++++--- 10 files changed, 289 insertions(+), 178 deletions(-) create mode 100644 robots/src/gui/GameController.java create mode 100644 robots/src/gui/GameModel.java create mode 100644 robots/src/gui/KeyEventListener.java create mode 100644 robots/src/gui/MathModule.java create mode 100644 robots/src/gui/Moveable.java diff --git a/robots/src/gui/Constants.java b/robots/src/gui/Constants.java index fa6a71b80..c8329d4f2 100644 --- a/robots/src/gui/Constants.java +++ b/robots/src/gui/Constants.java @@ -72,4 +72,8 @@ class GameVisualizerConstants { class LoggerConstants { public static final int LOGGER_IQUEUE_LENGTH = 100; } + + class KeyEventListenerConstants { + public static final double TARGET_VELOCITY = 0.1; + } } diff --git a/robots/src/gui/GameController.java b/robots/src/gui/GameController.java new file mode 100644 index 000000000..490771825 --- /dev/null +++ b/robots/src/gui/GameController.java @@ -0,0 +1,75 @@ +package gui; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.Timer; +import java.util.TimerTask; + +import static gui.Constants.GameVisualizerConstants.*; + +public class GameController extends JPanel { + + private final Robot robot; + private final Target target; + + public GameController() { + robot = new Robot(ROBOT_INITIAL_X_COORDINATE, ROBOT_INITIAL_Y_COORDINATE, ROBOT_INITIAL_DIRECTION); + target = new Target(TARGET_INITIAL_X_COORDINATE, TARGET_INITIAL_Y_COORDINATE); + java.util.Timer timer = new Timer(TIMER_NAME, true); + timer.schedule(new TimerTask() { + @Override + public void run() { + onRedrawEvent(); + } + }, TIMER_DELAY, TIMER_REDRAW_PERIOD); + timer.schedule(new TimerTask() { + @Override + public void run() { + onModelUpdateEvent(); + } + }, TIMER_DELAY, TIMER_UPDATE_PERIOD); + this.setFocusable(true); + this.requestFocus(); + addKeyListener(new KeyEventListener(target)); + addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent event) { + target.setxCoordinate(event.getPoint().x); + target.setyCoordinate(event.getPoint().y); + repaint(); + } + }); + setDoubleBuffered(true); + } + + private void onRedrawEvent() { + EventQueue.invokeLater(this::repaint); + } + + private void onModelUpdateEvent() { + double distance = MathModule.calculateDistance(target.getxCoordinate(), target.getyCoordinate(), robot.getxCoordinate(), robot.getyCoordinate()); + target.move(); + if (distance < ROBOT_STOP_DISTANCE) { + return; + } + double angleToTarget = MathModule.calculateAngle(robot.getxCoordinate(), robot.getyCoordinate(), target.getxCoordinate(), target.getyCoordinate()); + robot.setAngularVelocity(0); + if (angleToTarget > robot.getDirection()) { + robot.setAngularVelocity(MAX_ANGULAR_VELOCITY); + } + if (angleToTarget < robot.getDirection()) { + robot.setAngularVelocity(-MAX_ANGULAR_VELOCITY); + } + robot.move(); + } + + @Override + public void paint(Graphics g) { + super.paint(g); + Graphics2D g2d = (Graphics2D) g; + robot.draw(g2d); + target.draw(g2d); + } +} diff --git a/robots/src/gui/GameModel.java b/robots/src/gui/GameModel.java new file mode 100644 index 000000000..5b9ad683e --- /dev/null +++ b/robots/src/gui/GameModel.java @@ -0,0 +1,32 @@ +package gui; + +import java.awt.*; + +public abstract class GameModel { + + private double xCoordinate; + private double yCoordinate; + + public GameModel(double xCoordinate, double yCoordinate) { + this.xCoordinate = xCoordinate; + this.yCoordinate = yCoordinate; + } + + public double getxCoordinate() { + return xCoordinate; + } + + public void setxCoordinate(double xCoordinate) { + this.xCoordinate = xCoordinate; + } + + public double getyCoordinate() { + return yCoordinate; + } + + public void setyCoordinate(double yCoordinate) { + this.yCoordinate = yCoordinate; + } + + public abstract void draw(Graphics2D g); +} diff --git a/robots/src/gui/GameVisualizer.java b/robots/src/gui/GameVisualizer.java index 3ce1acb67..a1001e09f 100644 --- a/robots/src/gui/GameVisualizer.java +++ b/robots/src/gui/GameVisualizer.java @@ -1,156 +1,14 @@ package gui; -import java.awt.Color; -import java.awt.EventQueue; import java.awt.Graphics; -import java.awt.Graphics2D; -import java.awt.Point; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.awt.geom.AffineTransform; -import java.util.Timer; -import java.util.TimerTask; -import javax.swing.JPanel; +public class GameVisualizer { -import static gui.Constants.GameVisualizerConstants.*; - -public class GameVisualizer extends JPanel { - private final Robot robot; - private final Target target; - - public GameVisualizer() { - robot = new Robot(ROBOT_INITIAL_X_COORDINATE, ROBOT_INITIAL_Y_COORDINATE, ROBOT_INITIAL_DIRECTION); - target = new Target(TARGET_INITIAL_X_COORDINATE, TARGET_INITIAL_Y_COORDINATE); - Timer timer = new Timer(TIMER_NAME, true); - timer.schedule(new TimerTask() { - @Override - public void run() { - onRedrawEvent(); - } - }, TIMER_DELAY, TIMER_REDRAW_PERIOD); - timer.schedule(new TimerTask() { - @Override - public void run() { - onModelUpdateEvent(); - } - }, TIMER_DELAY, TIMER_UPDATE_PERIOD); - addMouseListener(new MouseAdapter() { - @Override - public void mouseClicked(MouseEvent event) { - setTargetPosition(event.getPoint()); - repaint(); - } - }); - setDoubleBuffered(true); - } - - private void setTargetPosition(Point point) { - target.setXCoordinate(point.x); - target.setYCoordinate(point.y); - } - - private void onRedrawEvent() { - EventQueue.invokeLater(this::repaint); - } - - private double calculateDistance(double firstPointXCoordinate, double firstPointYCoordinate, double secondPointXCoordinate, double secondPointYCoordinate) { - double xCoordinateDifference = firstPointXCoordinate - secondPointXCoordinate; - double yCoordinateDifference = firstPointYCoordinate - secondPointYCoordinate; - return Math.sqrt(xCoordinateDifference * xCoordinateDifference + yCoordinateDifference * yCoordinateDifference); - } - - private double calculateAngle(double startXCoordinate, double startYCoordinate, double endXCoordinate, double endYCoordinate) { - double xCoordinateDifference = endXCoordinate - startXCoordinate; - double yCoordinateDifference = endYCoordinate - startYCoordinate; - return asNormalizedRadians(Math.atan2(yCoordinateDifference, xCoordinateDifference)); - } - - private void onModelUpdateEvent() { - double distance = calculateDistance(target.getXCoordinate(), target.getYCoordinate(), robot.getXCoordinate(), robot.getYCoordinate()); - if (distance < ROBOT_STOP_DISTANCE) { - return; - } - double angleToTarget = calculateAngle(robot.getXCoordinate(), robot.getYCoordinate(), target.getXCoordinate(), target.getYCoordinate()); - double angularVelocity = 0; - if (angleToTarget > robot.getDirection()) { - angularVelocity = MAX_ANGULAR_VELOCITY; - } - if (angleToTarget < robot.getDirection()) { - angularVelocity = -MAX_ANGULAR_VELOCITY; - } - - moveRobot(angularVelocity); - } - - private void moveRobot(double angularVelocity) { - double newRobotDirection = robot.getDirection() + angularVelocity * TIMER_UPDATE_PERIOD; - double newRobotXCoordinate = robot.getXCoordinate() + ROBOT_VELOCITY / angularVelocity * - (Math.sin(newRobotDirection) - Math.sin(robot.getDirection())); - if (!Double.isFinite(newRobotXCoordinate)) { - newRobotXCoordinate = robot.getXCoordinate() + ROBOT_VELOCITY * TIMER_UPDATE_PERIOD * Math.cos(robot.getDirection()); - } - double newRobotYCoordinate = robot.getYCoordinate() - ROBOT_VELOCITY / angularVelocity * - (Math.cos(newRobotDirection) - Math.cos(robot.getDirection())); - if (!Double.isFinite(newRobotYCoordinate)) { - newRobotYCoordinate = robot.getYCoordinate() + ROBOT_VELOCITY * TIMER_UPDATE_PERIOD * Math.sin(robot.getDirection()); - } - robot.setXCoordinate(newRobotXCoordinate); - robot.setYCoordinate(newRobotYCoordinate); - robot.setDirection(asNormalizedRadians(newRobotDirection)); - } - - private double asNormalizedRadians(double angle) { - while (angle < 0) { - angle += 2 * Math.PI; - } - while (angle >= 2 * Math.PI) { - angle -= 2 * Math.PI; - } - return angle; - } - - private int round(double value) { - return (int) (value + 0.5); - } - - @Override - public void paint(Graphics g) { - super.paint(g); - Graphics2D g2d = (Graphics2D) g; - drawRobot(g2d, robot.getDirection()); - drawTarget(g2d, target.getXCoordinate(), target.getYCoordinate()); - } - - private void fillOval(Graphics g, int centerXCoordinate, int centerYCoordinate, int firstDiameter, int secondDiameter) { + public static void fillOval(Graphics g, int centerXCoordinate, int centerYCoordinate, int firstDiameter, int secondDiameter) { g.fillOval(centerXCoordinate - firstDiameter / 2, centerYCoordinate - secondDiameter / 2, firstDiameter, secondDiameter); } - private void drawOval(Graphics g, int centerXCoordinate, int centerYCoordinate, int firstDiameter, int secondDiameter) { + public static void drawOval(Graphics g, int centerXCoordinate, int centerYCoordinate, int firstDiameter, int secondDiameter) { g.drawOval(centerXCoordinate - firstDiameter / 2, centerYCoordinate - secondDiameter / 2, firstDiameter, secondDiameter); } - - private void drawRobot(Graphics2D g, double direction) { - int robotCenterX = round(robot.getXCoordinate()); - int robotCenterY = round(robot.getYCoordinate()); - AffineTransform t = AffineTransform.getRotateInstance(direction, robotCenterX, robotCenterY); - g.setTransform(t); - g.setColor(Color.MAGENTA); - fillOval(g, robotCenterX, robotCenterY, ROBOT_BODY_FIRST_DIAMETER, ROBOT_BODY_SECOND_DIAMETER); - g.setColor(Color.BLACK); - drawOval(g, robotCenterX, robotCenterY, ROBOT_BODY_FIRST_DIAMETER, ROBOT_BODY_SECOND_DIAMETER); - g.setColor(Color.WHITE); - fillOval(g, robotCenterX + ROBOT_HEAD_X_OFFSET, robotCenterY, ROBOT_HEAD_DIAMETER, ROBOT_HEAD_DIAMETER); - g.setColor(Color.BLACK); - drawOval(g, robotCenterX + ROBOT_HEAD_X_OFFSET, robotCenterY, ROBOT_HEAD_DIAMETER, ROBOT_HEAD_DIAMETER); - } - - private void drawTarget(Graphics2D g, int x, int y) { - AffineTransform t = AffineTransform.getRotateInstance(TARGET_THETA, TARGET_ANCHORX, TARGET_ANCHORY); - g.setTransform(t); - g.setColor(Color.GREEN); - fillOval(g, x, y, TARGET_DIAMETER, TARGET_DIAMETER); - g.setColor(Color.BLACK); - drawOval(g, x, y, TARGET_DIAMETER, TARGET_DIAMETER); - } } diff --git a/robots/src/gui/GameWindow.java b/robots/src/gui/GameWindow.java index 746404f27..df93e7aa2 100644 --- a/robots/src/gui/GameWindow.java +++ b/robots/src/gui/GameWindow.java @@ -11,9 +11,9 @@ public class GameWindow extends JInternalFrame { public GameWindow() { super(GAME_WINDOW_TITLE, true, true, true, true); - GameVisualizer m_visualizer = new GameVisualizer(); + GameController controller = new GameController(); JPanel panel = new JPanel(new BorderLayout()); - panel.add(m_visualizer, BorderLayout.CENTER); + panel.add(controller, BorderLayout.CENTER); getContentPane().add(panel); pack(); } diff --git a/robots/src/gui/KeyEventListener.java b/robots/src/gui/KeyEventListener.java new file mode 100644 index 000000000..772b73895 --- /dev/null +++ b/robots/src/gui/KeyEventListener.java @@ -0,0 +1,49 @@ +package gui; + +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; + +import static gui.Constants.KeyEventListenerConstants.TARGET_VELOCITY; + +public class KeyEventListener extends KeyAdapter { + + private final Target target; + + public KeyEventListener(Target target) { + this.target = target; + } + + @Override + public void keyTyped(KeyEvent e) { + keyPressed(e); + } + + @Override + public void keyPressed(KeyEvent e) { + int keyCode = e.getKeyCode(); + switch (keyCode) { + case KeyEvent.VK_DOWN: { + target.setVerticalVelocity(TARGET_VELOCITY); + break; + } + case KeyEvent.VK_UP: { + target.setVerticalVelocity(-TARGET_VELOCITY); + break; + } + case KeyEvent.VK_RIGHT: { + target.setHorizontalVelocity(TARGET_VELOCITY); + break; + } + case KeyEvent.VK_LEFT: { + target.setHorizontalVelocity(-TARGET_VELOCITY); + break; + } + } + } + + @Override + public void keyReleased(KeyEvent e) { + target.setHorizontalVelocity(0); + target.setVerticalVelocity(0); + } +} diff --git a/robots/src/gui/MathModule.java b/robots/src/gui/MathModule.java new file mode 100644 index 000000000..809e860c6 --- /dev/null +++ b/robots/src/gui/MathModule.java @@ -0,0 +1,30 @@ +package gui; + +public class MathModule { + + public static int round(double number) { + return (int) (number + 0.5); + } + + public static double calculateDistance(double firstPointXCoordinate, double firstPointYCoordinate, double secondPointXCoordinate, double secondPointYCoordinate) { + double xCoordinateDifference = firstPointXCoordinate - secondPointXCoordinate; + double yCoordinateDifference = firstPointYCoordinate - secondPointYCoordinate; + return Math.sqrt(xCoordinateDifference * xCoordinateDifference + yCoordinateDifference * yCoordinateDifference); + } + + public static double calculateAngle(double startXCoordinate, double startYCoordinate, double endXCoordinate, double endYCoordinate) { + double xCoordinateDifference = endXCoordinate - startXCoordinate; + double yCoordinateDifference = endYCoordinate - startYCoordinate; + return asNormalizedRadians(Math.atan2(yCoordinateDifference, xCoordinateDifference)); + } + + public static double asNormalizedRadians(double angle) { + while (angle < 0) { + angle += 2 * Math.PI; + } + while (angle >= 2 * Math.PI) { + angle -= 2 * Math.PI; + } + return angle; + } +} diff --git a/robots/src/gui/Moveable.java b/robots/src/gui/Moveable.java new file mode 100644 index 000000000..ffcdbd42d --- /dev/null +++ b/robots/src/gui/Moveable.java @@ -0,0 +1,6 @@ +package gui; + +public interface Moveable { + + void move(); +} diff --git a/robots/src/gui/Robot.java b/robots/src/gui/Robot.java index a0deaf678..da5140021 100644 --- a/robots/src/gui/Robot.java +++ b/robots/src/gui/Robot.java @@ -1,38 +1,68 @@ package gui; -public class Robot { +import java.awt.*; +import java.awt.geom.AffineTransform; + +import static gui.Constants.GameVisualizerConstants.*; +import static gui.Constants.GameVisualizerConstants.ROBOT_HEAD_DIAMETER; + +public class Robot extends GameModel implements Moveable { - private volatile double xCoordinate; - private volatile double yCoordinate; private volatile double direction; + private double angularVelocity; - public void setXCoordinate(double xCoordinate) { - this.xCoordinate = xCoordinate; + public double getAngularVelocity() { + return angularVelocity; } - public void setYCoordinate(double yCoordinate) { - this.yCoordinate = yCoordinate; + public void setAngularVelocity(double angularVelocity) { + this.angularVelocity = angularVelocity; } public void setDirection(double direction) { this.direction = direction; } - public double getXCoordinate() { - return xCoordinate; - } - - public double getYCoordinate() { - return yCoordinate; - } - public double getDirection() { return direction; } public Robot(double xCoordinate, double yCoordinate, double direction) { - this.xCoordinate = xCoordinate; - this.yCoordinate = yCoordinate; + super(xCoordinate, yCoordinate); this.direction = direction; } + + @Override + public void draw(Graphics2D g) { + int robotCenterX = MathModule.round(getxCoordinate()); + int robotCenterY = MathModule.round(getyCoordinate()); + AffineTransform t = AffineTransform.getRotateInstance(getDirection(), robotCenterX, robotCenterY); + g.setTransform(t); + g.setColor(Color.MAGENTA); + GameVisualizer.fillOval(g, robotCenterX, robotCenterY, ROBOT_BODY_FIRST_DIAMETER, ROBOT_BODY_SECOND_DIAMETER); + g.setColor(Color.BLACK); + GameVisualizer.drawOval(g, robotCenterX, robotCenterY, ROBOT_BODY_FIRST_DIAMETER, ROBOT_BODY_SECOND_DIAMETER); + g.setColor(Color.WHITE); + GameVisualizer.fillOval(g, robotCenterX + ROBOT_HEAD_X_OFFSET, robotCenterY, ROBOT_HEAD_DIAMETER, ROBOT_HEAD_DIAMETER); + g.setColor(Color.BLACK); + GameVisualizer.drawOval(g, robotCenterX + ROBOT_HEAD_X_OFFSET, robotCenterY, ROBOT_HEAD_DIAMETER, ROBOT_HEAD_DIAMETER); + } + + @Override + public void move() { + double newRobotDirection = getDirection() + getAngularVelocity() * TIMER_UPDATE_PERIOD; + double newRobotXCoordinate = getxCoordinate() + ROBOT_VELOCITY / getAngularVelocity() * + (Math.sin(newRobotDirection) - Math.sin(getDirection())); + if (!Double.isFinite(newRobotXCoordinate)) { + newRobotXCoordinate = getxCoordinate() + ROBOT_VELOCITY * TIMER_UPDATE_PERIOD * Math.cos(getDirection()); + } + double newRobotYCoordinate = getyCoordinate() - ROBOT_VELOCITY / getAngularVelocity() * + (Math.cos(newRobotDirection) - Math.cos(getDirection())); + if (!Double.isFinite(newRobotYCoordinate)) { + newRobotYCoordinate = getyCoordinate() + ROBOT_VELOCITY * TIMER_UPDATE_PERIOD * Math.sin(getDirection()); + } + setxCoordinate(newRobotXCoordinate); + setyCoordinate(newRobotYCoordinate); + setDirection(MathModule.asNormalizedRadians(newRobotDirection)); + } } diff --git a/robots/src/gui/Target.java b/robots/src/gui/Target.java index 46e9cd6a8..0f80b22a1 100644 --- a/robots/src/gui/Target.java +++ b/robots/src/gui/Target.java @@ -1,28 +1,55 @@ package gui; -public class Target { +import java.awt.*; +import java.awt.geom.AffineTransform; - private volatile int xCoordinate; - private volatile int yCoordinate; +import static gui.Constants.GameVisualizerConstants.*; +import static gui.Constants.GameVisualizerConstants.TARGET_DIAMETER; - public int getXCoordinate() { - return xCoordinate; +public class Target extends GameModel implements Moveable { + + private volatile double horizontalVelocity; + private volatile double verticalVelocity; + + public double getHorizontalVelocity() { + return horizontalVelocity; + } + + public void setHorizontalVelocity(double horizontalVelocity) { + this.horizontalVelocity = horizontalVelocity; + } + + public double getVerticalVelocity() { + return verticalVelocity; } - public void setXCoordinate(int xCoordinate) { - this.xCoordinate = xCoordinate; + public void setVerticalVelocity(double verticalVelocity) { + this.verticalVelocity = verticalVelocity; } - public int getYCoordinate() { - return yCoordinate; + public Target(double xCoordinate, double yCoordinate) { + super(xCoordinate, yCoordinate); + this.horizontalVelocity = 0; + this.verticalVelocity = 0; } - public void setYCoordinate(int yCoordinate) { - this.yCoordinate = yCoordinate; + @Override + public void draw(Graphics2D g) { + AffineTransform t = AffineTransform.getRotateInstance(TARGET_THETA, TARGET_ANCHORX, TARGET_ANCHORY); + g.setTransform(t); + g.setColor(Color.GREEN); + int targetCenterX = MathModule.round(getxCoordinate()); + int targetCenterY = MathModule.round(getyCoordinate()); + GameVisualizer.fillOval(g, targetCenterX, targetCenterY, TARGET_DIAMETER, TARGET_DIAMETER); + g.setColor(Color.BLACK); + GameVisualizer.drawOval(g, targetCenterX, targetCenterY, TARGET_DIAMETER, TARGET_DIAMETER); } - public Target(int xCoordinate, int yCoordinate) { - this.xCoordinate = xCoordinate; - this.yCoordinate = yCoordinate; + @Override + public void move() { + double newTargetXCoordinate = getxCoordinate() + getHorizontalVelocity() * TIMER_UPDATE_PERIOD; + double newTargetYCoordinate = getyCoordinate() + getVerticalVelocity() * TIMER_UPDATE_PERIOD; + setxCoordinate(newTargetXCoordinate); + setyCoordinate(newTargetYCoordinate); } } From db0f3230700292da64c7d13a9c1902378b12f542 Mon Sep 17 00:00:00 2001 From: crystaline Date: Mon, 15 May 2023 17:39:06 +0500 Subject: [PATCH 5/6] Fix robot movement --- robots/src/gui/GameController.java | 19 ++++++++++++++----- robots/src/gui/Robot.java | 12 ++---------- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/robots/src/gui/GameController.java b/robots/src/gui/GameController.java index 490771825..343af43db 100644 --- a/robots/src/gui/GameController.java +++ b/robots/src/gui/GameController.java @@ -55,12 +55,21 @@ private void onModelUpdateEvent() { return; } double angleToTarget = MathModule.calculateAngle(robot.getxCoordinate(), robot.getyCoordinate(), target.getxCoordinate(), target.getyCoordinate()); - robot.setAngularVelocity(0); - if (angleToTarget > robot.getDirection()) { - robot.setAngularVelocity(MAX_ANGULAR_VELOCITY); + if (angleToTarget == robot.getDirection()) { + robot.setAngularVelocity(0); + } else if (angleToTarget <= Math.PI) { + if (angleToTarget > robot.getDirection() || robot.getDirection() > angleToTarget + Math.PI) { + robot.setAngularVelocity(MAX_ANGULAR_VELOCITY); + } else { + robot.setAngularVelocity(-MAX_ANGULAR_VELOCITY); + } } - if (angleToTarget < robot.getDirection()) { - robot.setAngularVelocity(-MAX_ANGULAR_VELOCITY); + else { + if (angleToTarget > robot.getDirection() && robot.getDirection() > angleToTarget - Math.PI) { + robot.setAngularVelocity(MAX_ANGULAR_VELOCITY); + } else { + robot.setAngularVelocity(-MAX_ANGULAR_VELOCITY); + } } robot.move(); } diff --git a/robots/src/gui/Robot.java b/robots/src/gui/Robot.java index da5140021..bcfd156c7 100644 --- a/robots/src/gui/Robot.java +++ b/robots/src/gui/Robot.java @@ -51,16 +51,8 @@ public void draw(Graphics2D g) { @Override public void move() { double newRobotDirection = getDirection() + getAngularVelocity() * TIMER_UPDATE_PERIOD; - double newRobotXCoordinate = getxCoordinate() + ROBOT_VELOCITY / getAngularVelocity() * - (Math.sin(newRobotDirection) - Math.sin(getDirection())); - if (!Double.isFinite(newRobotXCoordinate)) { - newRobotXCoordinate = getxCoordinate() + ROBOT_VELOCITY * TIMER_UPDATE_PERIOD * Math.cos(getDirection()); - } - double newRobotYCoordinate = getyCoordinate() - ROBOT_VELOCITY / getAngularVelocity() * - (Math.cos(newRobotDirection) - Math.cos(getDirection())); - if (!Double.isFinite(newRobotYCoordinate)) { - newRobotYCoordinate = getyCoordinate() + ROBOT_VELOCITY * TIMER_UPDATE_PERIOD * Math.sin(getDirection()); - } + double newRobotXCoordinate = getxCoordinate() + ROBOT_VELOCITY * TIMER_UPDATE_PERIOD * Math.cos(getDirection()); + double newRobotYCoordinate = getyCoordinate() + ROBOT_VELOCITY * TIMER_UPDATE_PERIOD * Math.sin(getDirection()); setxCoordinate(newRobotXCoordinate); setyCoordinate(newRobotYCoordinate); setDirection(MathModule.asNormalizedRadians(newRobotDirection)); From d3786d07a35e8dd51042beb3ff57cc372be1ea80 Mon Sep 17 00:00:00 2001 From: AndShaA Date: Fri, 19 May 2023 16:53:07 +0500 Subject: [PATCH 6/6] Made a logging of the robot's movement --- robots/src/gui/Constants.java | 2 ++ robots/src/gui/GameController.java | 19 ++++++++++++++++--- robots/src/gui/GameWindow.java | 4 ++-- robots/src/gui/MainApplicationFrame.java | 14 ++++++++++++-- robots/src/gui/Robot.java | 3 +++ 5 files changed, 35 insertions(+), 7 deletions(-) diff --git a/robots/src/gui/Constants.java b/robots/src/gui/Constants.java index c8329d4f2..94081276f 100644 --- a/robots/src/gui/Constants.java +++ b/robots/src/gui/Constants.java @@ -25,6 +25,8 @@ class MainApplicationFrameConstants { public static final String STYLE_MENU_TEXT = "Режим отображения"; public static final String CROSS_PLATFORM_STYLE_TEXT = "Универсальная схема"; public static final String SYSTEM_STYLE_TEXT = "Системная схема"; + public static final String ROBOT_START_MOVING = "Робот двигается"; + public static final String ROBOT_STOP_MOVING = "Робот остановился"; } class RobotsProgramConstants { diff --git a/robots/src/gui/GameController.java b/robots/src/gui/GameController.java index 343af43db..c77fe1c1d 100644 --- a/robots/src/gui/GameController.java +++ b/robots/src/gui/GameController.java @@ -8,15 +8,18 @@ import java.util.TimerTask; import static gui.Constants.GameVisualizerConstants.*; +import static gui.MainApplicationFrame.updateStateRobot; public class GameController extends JPanel { private final Robot robot; private final Target target; + private boolean isMoving; - public GameController() { - robot = new Robot(ROBOT_INITIAL_X_COORDINATE, ROBOT_INITIAL_Y_COORDINATE, ROBOT_INITIAL_DIRECTION); - target = new Target(TARGET_INITIAL_X_COORDINATE, TARGET_INITIAL_Y_COORDINATE); + public GameController(Robot robot, Target target) { + this.robot = robot; + this.target = target; + isMoving = false; java.util.Timer timer = new Timer(TIMER_NAME, true); timer.schedule(new TimerTask() { @Override @@ -51,9 +54,19 @@ private void onRedrawEvent() { private void onModelUpdateEvent() { double distance = MathModule.calculateDistance(target.getxCoordinate(), target.getyCoordinate(), robot.getxCoordinate(), robot.getyCoordinate()); target.move(); + if (distance < ROBOT_STOP_DISTANCE) { + + if (isMoving) { + isMoving = false; + updateStateRobot(isMoving); + } return; } + if (!isMoving) { + isMoving = true; + updateStateRobot(isMoving); + } double angleToTarget = MathModule.calculateAngle(robot.getxCoordinate(), robot.getyCoordinate(), target.getxCoordinate(), target.getyCoordinate()); if (angleToTarget == robot.getDirection()) { robot.setAngularVelocity(0); diff --git a/robots/src/gui/GameWindow.java b/robots/src/gui/GameWindow.java index df93e7aa2..d1c967ba3 100644 --- a/robots/src/gui/GameWindow.java +++ b/robots/src/gui/GameWindow.java @@ -9,9 +9,9 @@ public class GameWindow extends JInternalFrame { - public GameWindow() { + public GameWindow(Robot robot, Target target) { super(GAME_WINDOW_TITLE, true, true, true, true); - GameController controller = new GameController(); + GameController controller = new GameController(robot, target); JPanel panel = new JPanel(new BorderLayout()); panel.add(controller, BorderLayout.CENTER); getContentPane().add(panel); diff --git a/robots/src/gui/MainApplicationFrame.java b/robots/src/gui/MainApplicationFrame.java index fb99aa79d..04dbd16a3 100644 --- a/robots/src/gui/MainApplicationFrame.java +++ b/robots/src/gui/MainApplicationFrame.java @@ -8,6 +8,7 @@ import log.Logger; +import static gui.Constants.GameVisualizerConstants.*; import static gui.Constants.MainApplicationFrameConstants.*; public class MainApplicationFrame extends JFrame { @@ -20,12 +21,13 @@ public MainApplicationFrame() { screenSize.height - SCREEN_OFFSET * 2); setContentPane(desktopPane); - + Robot robot = new Robot(ROBOT_INITIAL_X_COORDINATE, ROBOT_INITIAL_Y_COORDINATE, ROBOT_INITIAL_DIRECTION); + Target target = new Target(TARGET_INITIAL_X_COORDINATE, TARGET_INITIAL_Y_COORDINATE); LogWindow logWindow = createLogWindow(); addWindow(logWindow); - GameWindow gameWindow = new GameWindow(); + GameWindow gameWindow = new GameWindow(robot, target); gameWindow.setSize(INITIAL_GAME_WINDOW_WIDTH, INITIAL_GAME_WINDOW_HEIGHT); addWindow(gameWindow); @@ -81,8 +83,16 @@ private JMenu createTestMenu() { addLogMessageItem.addActionListener(event -> Logger.debug(TEST_LOG_TEXT)); testMenu.add(addLogMessageItem); + return testMenu; } + public static void updateStateRobot(boolean isRobotMoving) { + if (isRobotMoving) { + Logger.debug(ROBOT_START_MOVING); + } else { + Logger.debug(ROBOT_STOP_MOVING); + } + } private JMenu createLookAndFeelMenu() { JMenu styleMenu = new JMenu(STYLE_MENU_TEXT); diff --git a/robots/src/gui/Robot.java b/robots/src/gui/Robot.java index bcfd156c7..c6329ac89 100644 --- a/robots/src/gui/Robot.java +++ b/robots/src/gui/Robot.java @@ -2,6 +2,7 @@ import java.awt.*; import java.awt.geom.AffineTransform; +import java.util.ArrayList; import static gui.Constants.GameVisualizerConstants.*; import static gui.Constants.GameVisualizerConstants.ROBOT_HEAD_DIAMETER; @@ -56,5 +57,7 @@ public void move() { setxCoordinate(newRobotXCoordinate); setyCoordinate(newRobotYCoordinate); setDirection(MathModule.asNormalizedRadians(newRobotDirection)); + } + }