diff --git a/robots/pom.xml b/robots/pom.xml
new file mode 100644
index 000000000..6a1818672
--- /dev/null
+++ b/robots/pom.xml
@@ -0,0 +1,146 @@
+
+
+ 4.0.0
+
+ groupId
+ robots
+ 1.0-SNAPSHOT
+
+
+
+
+ com.googlecode.json-simple
+ json-simple
+ 1.1
+
+
+ commons-lang
+ commons-lang
+ 2.5
+
+
+ org.openjfx
+ javafx-base
+ 11
+ pom
+
+
+ org.apache.activemq
+ activemq-core
+ 5.3.0
+
+
+ com.intellij
+ forms_rt
+ 5.0
+
+
+
+ junit
+ junit
+ 4.7
+ test
+
+
+ org.jetbrains.kotlin
+ kotlin-stdlib
+ 1.7.10
+
+
+
+
+ 17
+ 17
+ UTF-8
+
+
+
+
+
+
+ maven-clean-plugin
+ 3.1.0
+
+
+ maven-resources-plugin
+ 3.0.2
+
+
+ maven-compiler-plugin
+ 3.8.0
+
+
+ 14
+
+
+
+ org.openjfx
+ javafx-maven-plugin
+ 0.0.3
+
+ src.main.java.RobotsProgram
+
+
+
+ org.apache.maven.plugins
+ maven-dependency-plugin
+
+
+ copy-dependencies
+ prepare-package
+
+ copy-dependencies
+
+
+
+ ${project.build.directory}/libs
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+
+
+
+ true
+ libs/
+
+ RobotsProgram
+
+
+
+
+
+
+
+ maven-surefire-plugin
+ 2.22.1
+
+
+ maven-install-plugin
+ 2.5.2
+
+
+ maven-deploy-plugin
+ 2.8.2
+
+
+
+ maven-site-plugin
+ 3.7.1
+
+
+ maven-project-info-reports-plugin
+ 3.0.0
+
+
+
+
+
+
diff --git a/robots/src/gui/GameVisualizer.java b/robots/src/gui/GameVisualizer.java
index f82cfd8f8..cd67c2548 100644
--- a/robots/src/gui/GameVisualizer.java
+++ b/robots/src/gui/GameVisualizer.java
@@ -1,40 +1,35 @@
package gui;
-import java.awt.Color;
-import java.awt.EventQueue;
-import java.awt.Graphics;
-import java.awt.Graphics2D;
-import java.awt.Point;
+import javax.swing.*;
+import java.awt.*;
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 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 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()
+
+
+ public static final double maxVelocity = 0.1;
+ public static final double maxAngularVelocity = 0.001;
+
+ private final RobotController controller;
+
+ public GameVisualizer(RobotController controller)
{
+ this.controller = controller;
+ Timer m_timer = initTimer();
m_timer.schedule(new TimerTask()
{
@Override
@@ -68,7 +63,7 @@ protected void setTargetPosition(Point p)
m_targetPositionX = p.x;
m_targetPositionY = p.y;
}
-
+
protected void onRedrawEvent()
{
EventQueue.invokeLater(this::repaint);
@@ -80,39 +75,37 @@ private static double distance(double x1, double y1, double x2, double y2)
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 cornerTo(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,
+ controller.getPositionX(), controller.getPositionY());
+ if (distance < 0.5) {
return;
}
- double velocity = maxVelocity;
- double angleToTarget = angleTo(m_robotPositionX, m_robotPositionY, m_targetPositionX, m_targetPositionY);
+
+ double cornerToTarget = cornerTo(controller.getPositionX(), controller.getPositionY(), m_targetPositionX, m_targetPositionY);
double angularVelocity = 0;
- if (angleToTarget > m_robotDirection)
- {
+ if (cornerToTarget > controller.getDirection() + 0.01) {
angularVelocity = maxAngularVelocity;
}
- if (angleToTarget < m_robotDirection)
- {
+ if (cornerToTarget < controller.getDirection() - 0.01) {
angularVelocity = -maxAngularVelocity;
}
-
- moveRobot(velocity, angularVelocity, 10);
+
+
+ controller.move(maxVelocity, angularVelocity, 10);
+
}
-
- private static double applyLimits(double value, double min, double max)
+
+ static double applyLimits(double value, double min, double max)
{
if (value < min)
return min;
@@ -120,87 +113,49 @@ private static double applyLimits(double value, double min, double max)
return max;
return value;
}
-
- 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))
- {
- 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))
- {
- newY = m_robotPositionY + velocity * duration * Math.sin(m_robotDirection);
- }
- m_robotPositionX = newX;
- m_robotPositionY = newY;
- double newDirection = asNormalizedRadians(m_robotDirection + angularVelocity * duration);
- m_robotDirection = newDirection;
- }
- private static double asNormalizedRadians(double angle)
+
+ static double asNormalizedRadians(double corner)
{
- while (angle < 0)
+ while (corner < 0)
{
- angle += 2*Math.PI;
+ corner += 2*Math.PI;
}
- while (angle >= 2*Math.PI)
+ while (corner >= 2*Math.PI)
{
- angle -= 2*Math.PI;
+ corner -= 2*Math.PI;
}
- return angle;
+ return corner;
}
-
- private static int round(double value)
+
+ public static int round(double value)
{
return (int)(value + 0.5);
}
-
+
@Override
public void paint(Graphics g)
{
super.paint(g);
- Graphics2D g2d = (Graphics2D)g;
- drawRobot(g2d, round(m_robotPositionX), round(m_robotPositionY), m_robotDirection);
+ Graphics2D g2d = (Graphics2D)g;
+ controller.draw(g2d);
drawTarget(g2d, m_targetPositionX, m_targetPositionY);
}
-
- private static void fillOval(Graphics g, int centerX, int centerY, int diam1, int diam2)
+
+ 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)
+
+ 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);
- int robotCenterY = round(m_robotPositionY);
- 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);
- g.setColor(Color.BLACK);
- drawOval(g, robotCenterX + 10, robotCenterY, 5, 5);
- }
-
+
+
private void drawTarget(Graphics2D g, int x, int y)
{
- AffineTransform t = AffineTransform.getRotateInstance(0, 0, 0);
+ 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..8b0056181 100644
--- a/robots/src/gui/GameWindow.java
+++ b/robots/src/gui/GameWindow.java
@@ -1,17 +1,15 @@
package gui;
-import java.awt.BorderLayout;
-
-import javax.swing.JInternalFrame;
-import javax.swing.JPanel;
+import javax.swing.*;
+import java.awt.*;
public class GameWindow extends JInternalFrame
{
- private final GameVisualizer m_visualizer;
- public GameWindow()
+
+ public GameWindow(RobotController controller)
{
super("Игровое поле", true, true, true, true);
- m_visualizer = new GameVisualizer();
+ GameVisualizer m_visualizer = new GameVisualizer(controller);
JPanel panel = new JPanel(new BorderLayout());
panel.add(m_visualizer, BorderLayout.CENTER);
getContentPane().add(panel);
diff --git a/robots/src/gui/MainApplicationFrame.java b/robots/src/gui/MainApplicationFrame.java
index 62e943ee1..45195a445 100644
--- a/robots/src/gui/MainApplicationFrame.java
+++ b/robots/src/gui/MainApplicationFrame.java
@@ -1,132 +1,163 @@
package gui;
-import java.awt.Dimension;
-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 javax.swing.event.MenuEvent;
+import javax.swing.event.MenuListener;
+import java.awt.*;
+import java.awt.event.KeyEvent;
+import java.awt.event.WindowEvent;
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
import log.Logger;
+import org.json.simple.JSONObject;
+import org.json.simple.parser.JSONParser;
+import org.json.simple.parser.ParseException;
+
/**
* Что требуется сделать:
- * 1. Метод создания меню перегружен функционалом и трудно читается.
+ * 1. Метод создания меню перегружен функционалом и трудно читается.
* Следует разделить его на серию более простых методов (или вообще выделить отдельный класс).
- *
*/
-public class MainApplicationFrame extends JFrame
-{
+public class MainApplicationFrame extends JFrame {
+
private final JDesktopPane desktopPane = new JDesktopPane();
-
+ RobotModel robotModel = new RobotModel();
+ RobotView robotView = new RobotView();
+ RobotController controller = new RobotController(robotModel, robotView);
+ private final GameWindow gameWindow = new GameWindow(controller);
+ private final LogWindow logWindow = createLogWindow();
+
+ RobotCoordinates coordinatesWindow = new RobotCoordinates();
public MainApplicationFrame() {
- //Make the big window be indented 50 pixels from each edge
- //of the screen.
- int inset = 50;
+//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);
+ screenSize.width - inset * 2,
+ screenSize.height - inset * 2);
setContentPane(desktopPane);
-
-
- LogWindow logWindow = createLogWindow();
- addWindow(logWindow);
- GameWindow gameWindow = new GameWindow();
- gameWindow.setSize(400, 400);
- addWindow(gameWindow);
+ addWindow(logWindow, 200, 200);
+ addWindow(gameWindow, 400, 400);
+ addWindow(coordinatesWindow, 300, 300);
+ robotModel.addObserver(coordinatesWindow);
+ applyConfig();
+ saveConfiguration();
setJMenuBar(generateMenuBar());
- setDefaultCloseOperation(EXIT_ON_CLOSE);
+ setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
+ addWindowListener(new java.awt.event.WindowAdapter() {
+
+ @Override
+ public void windowClosing(WindowEvent e) {
+ saveConfiguration();
+ exitProgram();
+ }
+ });
+
}
-
- 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, int height, int width) {
+ frame.setSize(width, height);
desktopPane.add(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()
- {
+
+// 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();
-
+
+ menuBar.add(createLookAndFeelMenu());
+ menuBar.add(createTestMenu());
+ menuBar.add(createExitMenu());
+
+ return menuBar;
+ }
+
+
+ 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);
- }
+ lookAndFeelMenu.getAccessibleContext().
- {
- JMenuItem crossplatformLookAndFeel = new JMenuItem("Универсальная схема", KeyEvent.VK_S);
- crossplatformLookAndFeel.addActionListener((event) -> {
- setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
- this.invalidate();
- });
- lookAndFeelMenu.add(crossplatformLookAndFeel);
- }
+ setAccessibleDescription(
+ "Управление режимом отображения приложения");
+
+
+ lookAndFeelMenu.add(
+ createJMenuItem("Системная схема",
+ KeyEvent.VK_S,
+ UIManager.getSystemLookAndFeelClassName()));
+
+ lookAndFeelMenu.add(createJMenuItem("Универсальная схема",
+ KeyEvent.VK_S,
+ UIManager.getCrossPlatformLookAndFeelClassName()));
+
+ return lookAndFeelMenu;
+
+ }
+
+ private JMenuItem createJMenuItem(String name, int key, String action) {
+ JMenuItem item = new JMenuItem(name, key);
+ item.addActionListener((event) -> {
+ setLookAndFeel(action);
+ this.invalidate();
+ });
+ return item;
+ }
+
+ 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) -> {
@@ -134,23 +165,117 @@ private JMenuBar generateMenuBar()
});
testMenu.add(addLogMessageItem);
}
+ return testMenu;
+ }
- menuBar.add(lookAndFeelMenu);
- menuBar.add(testMenu);
- return menuBar;
+
+ private JMenu createExitMenu() {
+ JMenu exitMenu = new JMenu("Выход");
+ exitMenu.setMnemonic(KeyEvent.VK_E);
+ exitMenu.getAccessibleContext().setAccessibleDescription(
+ "Выход из приложения");
+
+
+ exitMenu.addMenuListener(new MenuListener() {
+ @Override
+ public void menuSelected(MenuEvent e) {
+ exitProgram();
+ }
+
+ @Override
+ public void menuDeselected(MenuEvent e) {
+ }
+
+ @Override
+ public void menuCanceled(MenuEvent e) {
+ }
+ });
+
+ return exitMenu;
}
-
- private void setLookAndFeel(String className)
- {
- try
- {
+
+
+ public void exitProgram() {
+ int reply = JOptionPane.showConfirmDialog(null,
+ "Вы уверены что хотите выйти?", "Robots ", JOptionPane.YES_NO_OPTION);
+ int yesNoOption = JOptionPane.YES_NO_OPTION;
+
+ if (reply == JOptionPane.YES_OPTION) {
+
+ System.exit(0);
+
+ }
+ }
+ //локализация переменных
+ {UIManager.put("OptionPane.yesButtonText","Да");
+ UIManager.put("OptionPane.noButtonText","Нет");}
+
+ protected void saveConfiguration() {
+ JSONObject json = new JSONObject();
+ json.put("gameWindowX", gameWindow.getX());
+ json.put("gameWindowY", gameWindow.getY());
+ json.put("gameWindowWidth", gameWindow.getWidth());
+ json.put("gameWindowHeight", gameWindow.getHeight());
+
+ json.put("logWindowX", logWindow.getX());
+ json.put("logWindowY", logWindow.getY());
+ json.put("logWindowWidth", logWindow.getWidth());
+ json.put("logWindowHeight", logWindow.getHeight());
+
+ json.put("RobotCoordinatesX", coordinatesWindow.getX());
+ json.put("RobotCoordinatesY", coordinatesWindow.getY());
+ json.put("RobotCoordinatesWidth", coordinatesWindow.getWidth());
+ json.put("RobotCoordinatesHeight", coordinatesWindow.getHeight());
+// Save JSON to user home directory
+ String userHomeDir = System.getProperty("user.home");
+ File robotsDir = new File(userHomeDir + "/.Robots");
+ if (!robotsDir.exists()) {
+ robotsDir.mkdir();
+ }
+ File configFile = new File(userHomeDir + "/.Robots/config.json");
+ try (FileWriter fileWriter = new FileWriter(configFile.getAbsolutePath())) {
+ fileWriter.write(json.toString());
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ protected void applyConfig() {
+ JSONObject config = readFromConfig();
+ if (config == null) {
+ return;
+ }
+ gameWindow.setSize(((Long) config.get("gameWindowWidth")).intValue(), ((Long) config.get("gameWindowHeight")).intValue());
+ logWindow.setSize(((Long) config.get("logWindowWidth")).intValue(), ((Long) config.get("logWindowHeight")).intValue());
+ coordinatesWindow.setSize(((Long) config.get("RobotCoordinatesWidth")).intValue(), ((Long) config.get("RobotCoordinatesHeight")).intValue());
+ gameWindow.setLocation(((Long) config.get("gameWindowX")).intValue(), ((Long) config.get("gameWindowY")).intValue());
+ logWindow.setLocation(((Long) config.get("logWindowX")).intValue(), ((Long) config.get("logWindowY")).intValue());
+ coordinatesWindow.setLocation(((Long) config.get("RobotCoordinatesX")).intValue(), ((Long) config.get("RobotCoordinatesY")).intValue());
+ }
+
+ protected JSONObject readFromConfig() {
+
+ File configFile = new File(System.getProperty("user.home") + "/.Robots/config.json");
+ JSONParser parser = new JSONParser();
+ if (configFile.exists()) {
+ try {
+ return (JSONObject) parser.parse(new FileReader(configFile));
+ } catch (IOException e) {
+ e.printStackTrace();
+ } catch (ParseException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ return null;
+ }
+
+ private void setLookAndFeel(String className) {
+ try {
UIManager.setLookAndFeel(className);
SwingUtilities.updateComponentTreeUI(this);
- }
- catch (ClassNotFoundException | InstantiationException
- | IllegalAccessException | UnsupportedLookAndFeelException e)
- {
- // just ignore
+ } catch (ClassNotFoundException | InstantiationException
+ | IllegalAccessException | UnsupportedLookAndFeelException e) {
+// just ignore
}
}
}
diff --git a/robots/src/gui/RobotController.java b/robots/src/gui/RobotController.java
new file mode 100644
index 000000000..cf57c0ebb
--- /dev/null
+++ b/robots/src/gui/RobotController.java
@@ -0,0 +1,41 @@
+package gui;
+
+import java.awt.*;
+
+public class RobotController
+{
+ public RobotController (RobotModel robotModel, RobotView robotView )
+ {
+ this.robotModel = robotModel;
+ this.robotView = robotView;
+ }
+ private final RobotView robotView;
+ private final RobotModel robotModel;
+
+
+ public double getPositionX()
+ {
+ return robotModel.getPositionX();
+ }
+
+ public double getPositionY()
+ {
+ return robotModel.getPositionY();
+ }
+
+ public double getDirection()
+ {
+ return robotModel.getDirection();
+ }
+
+ public void move(double velocity, double angularVelocity, double duration)
+ {
+ robotModel.move(velocity, angularVelocity, duration);
+ }
+
+ public void draw(Graphics2D g)
+ {
+ robotView.draw(g, robotModel);
+ }
+
+}
\ No newline at end of file
diff --git a/robots/src/gui/RobotCoordinates.java b/robots/src/gui/RobotCoordinates.java
new file mode 100644
index 000000000..f4a862084
--- /dev/null
+++ b/robots/src/gui/RobotCoordinates.java
@@ -0,0 +1,38 @@
+package gui;
+
+import javafx.util.Pair;
+
+import javax.swing.*;
+import java.awt.*;
+import java.util.Observable;
+import java.util.Observer;
+
+public class RobotCoordinates extends JInternalFrame implements Observer
+{
+ private final TextArea coordinatesArea;
+
+ public RobotCoordinates()
+ {
+ super("Координаты робота", true, true, true, true);
+
+ coordinatesArea = new TextArea();
+ coordinatesArea.setSize(100, 50);
+
+ JPanel panel = new JPanel(new BorderLayout());
+ panel.add(coordinatesArea, BorderLayout.CENTER);
+ getContentPane().add(panel);
+ pack();
+ }
+
+ private void updateDisplayedRobotCoordinates(Pair newCoordinates)
+ {
+ coordinatesArea.setText("X: " + newCoordinates.getKey() + "\nY: " + newCoordinates.getValue());
+ coordinatesArea.invalidate();
+ }
+
+ @Override
+ public void update(Observable observable, Object o)
+ {
+ updateDisplayedRobotCoordinates((Pair) o);
+ }
+}
\ No newline at end of file
diff --git a/robots/src/gui/RobotModel.java b/robots/src/gui/RobotModel.java
new file mode 100644
index 000000000..63b2cd852
--- /dev/null
+++ b/robots/src/gui/RobotModel.java
@@ -0,0 +1,67 @@
+package gui;
+
+import javafx.util.Pair;
+
+import java.util.Observable;
+
+import static gui.GameVisualizer.*;
+
+public class RobotModel extends Observable
+{
+ private volatile double _positionX = 100;
+ public double getPositionX()
+ {
+ return _positionX;
+ }
+ public void setPositionX(double positionX)
+ {
+ _positionX = positionX;
+ }
+
+ private volatile double _positionY = 100;
+ public double getPositionY()
+ {
+ return _positionY;
+ }
+ public void setPositionY(double positionY)
+ {
+ _positionY = positionY;
+ }
+
+ private volatile double _direction = 0;
+ public double getDirection()
+ {
+ return _direction;
+ }
+ public void setDirection(double direction)
+ {
+ _direction = direction;
+ }
+
+ public void move(double velocity, double angularVelocity, double duration)
+ {
+ velocity = applyLimits(velocity, 0, maxVelocity);
+ angularVelocity = applyLimits(angularVelocity, -maxAngularVelocity, maxAngularVelocity);
+
+ var newX = _positionX;
+ var newY = _positionY;
+ var newDirection = _direction;
+
+ if (angularVelocity == 0)
+ {
+ newX = _positionX + velocity * duration * Math.cos(_direction);
+ newY = _positionY + velocity * duration * Math.sin(_direction);
+ }
+ else
+ {
+ newDirection = asNormalizedRadians(_direction + angularVelocity * duration);
+ }
+
+ setPositionX(newX);
+ setPositionY(newY);
+ setDirection(newDirection);
+
+ setChanged();
+ notifyObservers(new Pair<>(newX, newY));
+ }
+}
\ No newline at end of file
diff --git a/robots/src/gui/RobotView.java b/robots/src/gui/RobotView.java
new file mode 100644
index 000000000..5d124cc21
--- /dev/null
+++ b/robots/src/gui/RobotView.java
@@ -0,0 +1,26 @@
+package gui;
+
+import java.awt.*;
+import java.awt.geom.AffineTransform;
+
+
+import static gui.GameVisualizer.*;
+
+public class RobotView
+{
+ public void draw(Graphics2D g, RobotModel robotModel)
+ {
+ int robotCenterX = round(robotModel.getPositionX());
+ int robotCenterY = round(robotModel.getPositionY());
+ AffineTransform t = AffineTransform.getRotateInstance(robotModel.getDirection(), 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);
+ g.setColor(Color.BLACK);
+ drawOval(g, robotCenterX + 10, robotCenterY, 5, 5);
+ }
+}
\ No newline at end of file