/**
* @file .java
* @author Anderson Antunes <anderson.utf@gmail.com>
* *seu nome* <*seu email*>
* @version 1.0
*
* @section LICENSE
*
* Copyright (C) 2013 by Anderson Antunes <anderson.utf@gmail.com>
* *seu nome* <*seu email*>
*
* RobotInterface is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* RobotInterface is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* RobotInterface. If not, see <http://www.gnu.org/licenses/>.
*
*/
package robotinterface.gui.panels;
import java.awt.BasicStroke;
import robotinterface.drawable.util.QuickFrame;
import robotinterface.drawable.DrawingPanel;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Polygon;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.geom.Area;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.awt.geom.Path2D;
import java.awt.geom.Point2D;
import java.io.Serializable;
import java.util.ArrayList;
import robotinterface.util.trafficsimulator.Timer;
import robotinterface.robot.Robot;
import static java.lang.Math.*;
import java.util.Iterator;
import robotinterface.gui.panels.sidepanel.Item;
import robotinterface.gui.panels.sidepanel.SidePanel;
import robotinterface.robot.device.IRProximitySensor;
import robotinterface.robot.simulation.Environment;
import robotinterface.util.LineIterator;
/**
* Painel da simulação do robô. <### EM DESENVOLVIMENTO ###>
*/
public class SimulationPanel extends DrawingPanel implements Serializable {
public static final Item ITEM_LINE;
public static final Item ITEM_LINE_POLI;
public static final Item ITEM_OBSTACLE_LINE;
public static final Item ITEM_OBSTACLE_POLI;
public static final Item ITEM_REMOVE_LINE;
static {
Area myShape = new Area();
Polygon tmpShape = new Polygon();
tmpShape.addPoint(2, 0);
tmpShape.addPoint(20, 18);
tmpShape.addPoint(18, 20);
tmpShape.addPoint(0, 2);
myShape.add(new Area(tmpShape));
tmpShape.reset();
tmpShape.addPoint(18, 0);
tmpShape.addPoint(20, 2);
tmpShape.addPoint(2, 20);
tmpShape.addPoint(0, 18);
myShape.add(new Area(tmpShape));
ITEM_REMOVE_LINE = new Item("Remover", myShape, Color.red, "Remove elementos na interseção com esta linha");
myShape = new Area();
tmpShape = new Polygon();
tmpShape.reset();
tmpShape.addPoint(0, 20);
tmpShape.addPoint(10, 0);
tmpShape.addPoint(20, 20);
myShape.add(new Area(tmpShape));
tmpShape.reset();
tmpShape.addPoint(5, 17);
tmpShape.addPoint(10, 6);
tmpShape.addPoint(15, 17);
myShape.exclusiveOr(new Area(tmpShape));
ITEM_OBSTACLE_POLI = new Item("Parede Fechada", myShape, Environment.getObstacleColor(), "Cria paredes em forma de um polígono");
ITEM_LINE = new Item("Linha", new Rectangle(0, 0, 20, 4), Color.DARK_GRAY, "Linha preta colocada no chão, detectada pelo sensor de refletância");
ITEM_LINE_POLI = new Item("Linha Fechada", myShape, Color.DARK_GRAY, "Cria linhas no chão em forma de um polígono");
ITEM_OBSTACLE_LINE = new Item("Parede", new Rectangle(0, 0, 20, 4), Environment.getObstacleColor(), "Parede ou obstáculo, detectado pelo sensor de distância");
}
private final ArrayList<Robot> robots = new ArrayList<>();
private Environment env = new Environment();
private Item itemSelected;
private Point2D.Double point = null;
private final Ellipse2D.Double circle = new Ellipse2D.Double();
private final Ellipse2D.Double dot = new Ellipse2D.Double();
private final Line2D.Double radius = new Line2D.Double();
private int poliSegments = 6;
SidePanel sidePanel;
public static Shape create(int i, double x, double y, double r, Path2D.Double poly) {
if (poly == null) {
poly = new Path2D.Double();
} else {
poly.reset();
}
double alpha = 1;
double theta = (2 * Math.PI) / i;
double tx = x + r * cos(alpha);
double ty = y + r * sin(alpha);
poly.moveTo(tx, ty);
for (int j = 0; j < i; j++) {
alpha += theta;
tx = x + r * cos(alpha);
ty = y + r * sin(alpha);
poly.lineTo((int) tx, (int) ty);
}
return poly;
}
public SimulationPanel() {
super.midMouseButtonResetView = false;
sidePanel = new SidePanel() {
@Override
protected void ItemSelected(Item item, Object ref) {
try {
if (itemSelected == item) {
itemSelected.setSelected(false);
itemSelected = null;
} else {
if (itemSelected != null) {
itemSelected.setSelected(false);
itemSelected = null;
}
itemSelected = item;
itemSelected.setSelected(true);
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
};
sidePanel.setColor(Color.decode("#9DCA1D"));//FF7070
sidePanel.add(ITEM_LINE);
sidePanel.add(ITEM_LINE_POLI);
sidePanel.add(ITEM_OBSTACLE_LINE);
sidePanel.add(ITEM_OBSTACLE_POLI);
// sp.add(ITEM_CILINDER);
sidePanel.add(ITEM_REMOVE_LINE);
add(sidePanel);
//mapeia a posição a cada x ms
Timer timer = new Timer(300) {
ArrayList<Robot> tmpBots = new ArrayList<>();
@Override
public void run() {
tmpBots.clear();
synchronized (robots) {
tmpBots.addAll(robots);
}
for (Robot robot : tmpBots) {
if (!(robot.getLeftWheelSpeed() == 0 && robot.getRightWheelSpeed() == 0)) {
robot.updateVirtualPerception();
}
// robot.setRightWheelSpeed(30);
// robot.setLeftWheelSpeed(-30);
if (this.getCount() % 20 == 0) {
// robot.setRightWheelSpeed(Math.random() * 100);
// robot.setLeftWheelSpeed(Math.random() * 100);
}
}
}
};
timer.setDisposable(false);
clock.addTimer(timer);
clock.setPaused(false);
}
private SimulationPanel(Environment e) {
env = e;
}
public void hideSidePanel(boolean b) {
sidePanel.setOpen(!b);
}
public Environment getEnv() {
return env;
}
public void setEnv(Environment env) {
this.env = env;
}
public void addRobot(Robot robot) {
synchronized (robots) {
robots.add(robot);
}
add(robot);
}
public void removeRobot(Robot robot) {
synchronized (robots) {
robots.remove(robot);
}
remove(robot);
}
public ArrayList<Robot> getRobots() {
return robots;
}
@Override
public int getDrawableLayer() {
return DrawingPanel.BACKGROUND_LAYER | DrawingPanel.DEFAULT_LAYER | DrawingPanel.TOP_LAYER;
}
@Override
public void drawBackground(Graphics2D g, GraphicAttributes ga, InputState in) {
super.drawBackground(g, ga, in);
env.draw(g);
}
@Override
public void drawTopLayer(Graphics2D g, GraphicAttributes ga, InputState in) {
if (sidePanel.getObjectBouds().contains(in.getMouse())) {
return;
}
if (in.isKeyPressed(KeyEvent.VK_CONTROL)) {
if (in.mouseClicked() && in.getMouseButton() == MouseEvent.BUTTON2) {
resetView();
}
if (itemSelected == ITEM_LINE_POLI || itemSelected == ITEM_OBSTACLE_POLI) {
zoomEnabled = false;
int wr = -in.getMouseWheelRotation();
if (poliSegments + wr >= 3 && poliSegments + wr < 16) {
poliSegments += wr;
}
} else {
Robot r = null;
int d = Integer.MAX_VALUE;
synchronized (robots) {
for (Robot robot : robots) {
int x = (int) (robot.getPosX() - in.getTransformedMouse().x);
int y = (int) (robot.getPosY() - in.getTransformedMouse().y);
int tmpD = (int) (Math.sqrt(x * x + y * y));
if (tmpD < d) {
d = tmpD;
r = robot;
}
}
}
r.setSelected(true);
if (in.isKeyPressed(KeyEvent.VK_R)) {
zoomEnabled = false;
int wr = -in.getMouseWheelRotation();
r.setTheta(r.getTheta() + wr / 3.0);
} else if (in.mouseClicked() && in.getMouseButton() == MouseEvent.BUTTON1) {
r.setLocation(in.getTransformedMouse().x, in.getTransformedMouse().y);
}
}
} else {
zoomEnabled = true;
}
if (itemSelected != null) {
if (itemSelected == ITEM_LINE || itemSelected == ITEM_OBSTACLE_LINE || itemSelected == ITEM_REMOVE_LINE) {
if (in.mouseClicked()) {
if (in.getMouseButton() == MouseEvent.BUTTON1) {
if (point != null) {
Line2D.Double line = new Line2D.Double(point, in.getTransformedMouse());
if (itemSelected == ITEM_LINE) {
env.addFollowLine(new double[]{line.x1, line.y1, line.x2, line.y2});
} else if (itemSelected == ITEM_OBSTACLE_LINE) {
env.addWall(new double[]{line.x1, line.y1, line.x2, line.y2});
} else if (itemSelected == ITEM_REMOVE_LINE) {
for (Iterator<Line2D.Double> it = env.linesIterator(); it.hasNext();) {
Line2D.Double s = it.next();
if (s.intersectsLine(line)) {
env.removeFollowLine(s);
it.remove();
}
}
for (Iterator<Line2D.Double> it = env.obstaclesIterator(); it.hasNext();) {
Line2D.Double s = it.next();
if (s.intersectsLine(line)) {
env.removeWall(s);
it.remove();
}
}
}
}
point = new Point2D.Double(in.getTransformedMouse().x, in.getTransformedMouse().y);
} else {
point = null;
}
}
} else if (itemSelected == ITEM_LINE_POLI || itemSelected == ITEM_OBSTACLE_POLI) {
boolean obstacle = (itemSelected == ITEM_OBSTACLE_POLI);
if (in.mouseClicked()) {
if (in.getMouseButton() == MouseEvent.BUTTON1) {
if (point != null) {
Point point2 = in.getTransformedMouse();
double r = point.distance(point2);
double alpha = Math.atan2(point2.y - point.y, point2.x - point.x);
double theta = (2 * Math.PI) / poliSegments;
double ix = point.x + r * cos(alpha);
double iy = point.y + r * sin(alpha);
double ox = ix;
double oy = iy;
double tx;
double ty;
for (int i = 0; i < poliSegments; i++) {
alpha += theta;
tx = point.x + r * cos(alpha);
ty = point.y + r * sin(alpha);
if (obstacle) {
env.addWall(new double[]{ox, oy, tx, ty});
} else {
env.addFollowLine(new double[]{ox, oy, tx, ty});
}
ox = tx;
oy = ty;
}
point = null;
return;
}
point = new Point2D.Double(in.getTransformedMouse().x, in.getTransformedMouse().y);
} else {
point = null;
}
}
}
}
}
public static final BasicStroke defaultStroke = new BasicStroke();
public static final BasicStroke dashedStroke = new BasicStroke(1.0f,
BasicStroke.CAP_ROUND,
BasicStroke.JOIN_MITER,
10.0f, new float[]{5}, 0.0f);
@Override
public void draw(Graphics2D g, GraphicAttributes ga, InputState in) {
synchronized (robots) {
for (Robot robot : robots) {
if (in.mouseClicked() && in.getMouseButton() == MouseEvent.BUTTON2) {
if (!in.isKeyPressed(KeyEvent.VK_CONTROL)) {
robot.setLocation(0, 0);
robot.setTheta(0);
}
}
double v1 = robot.getLeftWheelSpeed();
double v2 = robot.getRightWheelSpeed();
//desenha o caminho
if (v1 != v2) {
//calcula o raio
double r = Robot.size / 2 * ((v1 + v2) / (v1 - v2));
//calcula o centro (ortogonal à direção atual do robô)
double x, y;
if (r < 0) {
r *= -1;
x = (cos(-robot.getTheta() + PI / 2) * r + robot.getObjectBouds().x);
y = (-sin(-robot.getTheta() + PI / 2) * r + robot.getObjectBouds().y);
} else {
x = (cos(-robot.getTheta() - PI / 2) * r + robot.getObjectBouds().x);
y = (-sin(-robot.getTheta() - PI / 2) * r + robot.getObjectBouds().y);
}
g.setStroke(dashedStroke); //linha pontilhada
//desenha o circulo
g.setColor(Color.gray);
circle.setFrame(x - r, y - r, r * 2, r * 2);
try {
g.draw(circle);
} catch (Exception e) {
e.printStackTrace();
System.out.println(circle);
System.exit(0);
}
//desenha o raio
g.setColor(Color.magenta);
radius.setLine(robot.getObjectBouds().x, robot.getObjectBouds().y, x, y);
g.draw(radius);
g.setStroke(defaultStroke); //fim da linha pontilhada
//desenha o centro
dot.setFrame(x - 3, y - 3, 6, 6);
g.fill(dot);
}
}
}
// g.setColor(Color.red);
// synchronized (rpos) {
// paintPoints(g, rpos, 5);
// }
// g.setColor(Color.GREEN.brighter());
// synchronized (obstacle) {
// paintPoints(g, obstacle, 5);
// }
// per.draw(g);
g.setStroke(new BasicStroke(5));
g.setColor(Color.green);
if (point != null) {
if (itemSelected == ITEM_LINE || itemSelected == ITEM_OBSTACLE_LINE || itemSelected == ITEM_REMOVE_LINE) {
g.drawLine((int) point.x, (int) point.y, (int) in.getTransformedMouse().x, (int) in.getTransformedMouse().y);
} else if (itemSelected == ITEM_LINE_POLI || itemSelected == ITEM_OBSTACLE_POLI) {
Point point2 = in.getTransformedMouse();
double r = point.distance(point2);
double alpha = Math.atan2(point2.y - point.y, point2.x - point.x);
double theta = (2 * Math.PI) / poliSegments;
double ix = point.x + r * cos(alpha);
double iy = point.y + r * sin(alpha);
double ox = ix;
double oy = iy;
double tx;
double ty;
for (int i = 0; i < poliSegments; i++) {
alpha += theta;
tx = point.x + r * cos(alpha);
ty = point.y + r * sin(alpha);
g.drawLine((int) ox, (int) oy, (int) tx, (int) ty);
ox = tx;
oy = ty;
}
// g.drawLine((int) ix, (int) iy, (int) ox, (int) oy);
}
}
// g.setColor(Color.BLACK);
// for (Shape s : obstacles) {
// g.draw(s);
// }
g.setStroke(defaultStroke);
}
public void resetSimulation() {
// remove as percepções dos robos e retorna-os para a posicao (0,0)
for (Robot r : robots) {
r.reset();
}
// limpa o ambiente
env.clearEnvironment();
}
public static void main(String[] args) {
SimulationPanel p = new SimulationPanel();
QuickFrame.create(p, "Teste Simulação").addComponentListener(p);
Robot r = new Robot();
r.add(new IRProximitySensor());
r.setEnvironment(p.getEnv());
p.addRobot(r);
r = new Robot();
r.add(new IRProximitySensor());
r.setEnvironment(p.getEnv());
p.addRobot(r);
}
}