package trafficjams.model.factories;
import trafficjams.model.Numbers;
import trafficjams.model.classes.CrossRoad;
import trafficjams.model.classes.Road;
import trafficjams.model.interfaces.ICrossRoad;
import trafficjams.model.interfaces.IRoad;
import trafficjams.model.registers.RoadMap;
import trafficjams.model.trafficutils.CrossDirections;
import trafficjams.model.util.Point;
import java.util.ArrayList;
import java.util.HashSet;
/**
* Created by IntelliJ IDEA.
* User: Администратор
* Date: 13.11.11
* Time: 15:25
* To change this template use File | Settings | File Templates.
*/
public class RoadMapFactory {
static {
instance = new RoadMapFactory();
}
public static final int left = CrossDirections.left ;
public static final int right = CrossDirections.right ;
public static final int up = CrossDirections.up;
public static final int down = CrossDirections.down;
private static final RoadMapFactory instance;
public static RoadMapFactory getInstance(){
return instance;
}
public RoadMap makeRoadMap(int crossCount, int autoCount, int tryCount) throws Exception {
System.out.println("Try to get road map: "+(crossCount-tryCount+1));
RoadMap retVal = new RoadMap();
float gorLength = Numbers.mapWidth / (float)crossCount /*- 2*/ ;
float vertLength = Numbers.mapHeight / (float) crossCount /*- 2*/ ;
ICrossRoad[][] a_crossRoads= new ICrossRoad[crossCount][crossCount];
ArrayList<ICrossRoad> l_crossRoads = new ArrayList<ICrossRoad>(crossCount*crossCount);
ArrayList<IRoad> roads = new ArrayList<IRoad>();
Road r = null;
//создаем перекрестки и дороги ввиде сетки, уже смещенной
for (int i = 0 ; i<crossCount; ++i){
if (i==0){
for (int j = 0 ; j < crossCount; ++j){
CrossRoad currCross = new CrossRoad(retVal);
l_crossRoads.add(currCross);
currCross.setCoord(new Point(gorLength*i + gorLength*rand(), vertLength*j + vertLength * rand()));
a_crossRoads[i][j] = currCross;
if (j==0){
continue;
}
r = new Road(retVal);
roads.add(r);
r.setFCross(a_crossRoads[i][j-1]);
r.setLCross(currCross);
((CrossRoad)a_crossRoads[i][j-1]).setRoadForDirection(r, right);
currCross.setRoadForDirection(r, left);
}
continue;
}
for (int j = 0 ; j < crossCount; ++j){
CrossRoad currCross = new CrossRoad(retVal);
l_crossRoads.add(currCross);
currCross.setCoord(new Point(gorLength*i + gorLength*rand(), vertLength*j + vertLength * rand()));
a_crossRoads[i][j] = currCross;
r = new Road(retVal);
roads.add(r);
r.setFCross(a_crossRoads[i-1][j]);
r.setLCross(currCross);
((CrossRoad)a_crossRoads[i-1][j]).setRoadForDirection(r, down);
currCross.setRoadForDirection(r, up);
if (j==0){
continue;
}
r = new Road(retVal);
roads.add(r);
r.setFCross(a_crossRoads[i][j-1]);
r.setLCross(currCross);
((CrossRoad)a_crossRoads[i][j-1]).setRoadForDirection(r, right);
currCross.setRoadForDirection(r, left);
}
}
//убиваем координаты выходящие за границы(выставляем в границы)
this.removeNegatives(l_crossRoads);
//выбираем дороги для уничтожения
ArrayList<IRoad> toDelete = new ArrayList<IRoad>();
for (IRoad __r : roads){
if (Math.random()<Numbers.percent){
toDelete.add(__r);
}
}
//удаляем дороги
for (IRoad __r : toDelete){
if (!roads.remove(__r))
throw new Exception("дорога не удалилась!");
Road _r = (Road) __r;
((CrossRoad)_r.getFCross()).deleteRoad(_r);
((CrossRoad)_r.getLCross()).deleteRoad(_r);
}
//удаляем перекрестки в которые входят 0 или 2 дороги (и соединяем дорогий то что разъединилось)
for (int i = 0 ; i<crossCount ; ++i){
for (int j = 0 ; j<crossCount; ++j){
CrossRoad cr = (CrossRoad) a_crossRoads[i][j];
if (cr.getType() == 0){
if (!l_crossRoads.remove(a_crossRoads[i][j]))
throw new Exception("перекресток не удалился!");
a_crossRoads[i][j] = null;
}
if (cr.getType() == 2){
if (!l_crossRoads.remove(a_crossRoads[i][j]))
throw new Exception("перекресток не удалился!");
a_crossRoads[i][j] = null;
IRoad[] rd = cr.get2Roads();
CrossRoad c0 = (CrossRoad)((Road)rd[0]).getOtherEnd(cr);
CrossRoad c1 = (CrossRoad)((Road)rd[1]).getOtherEnd(cr);
if (!(roads.remove(rd[0]) && roads.remove(rd[1])))
throw new Exception("road remove error");
IRoad newRoad = this.createConnector(c0,c1);
roads.add(newRoad);
((CrossRoad) c0).setRoadForDirection(newRoad, c0.deleteRoad(rd[0]));
((CrossRoad) c1).setRoadForDirection(newRoad, c1.deleteRoad(rd[1]));
}
}
}
//проверяем на связность
if (!this.isCorrect(l_crossRoads, roads, tryCount)){
return this.makeRoadMap(crossCount,autoCount,--tryCount);
}
//проверяем перекрестки и инициализируем очереди
for (ICrossRoad iCrossRoad: l_crossRoads){
CrossRoad crossRoad = (CrossRoad) iCrossRoad;
if (crossRoad.getType()==2){
return this.makeRoadMap(crossCount, autoCount, --tryCount);
}
crossRoad.initQueues();
}
///выставляем длины дорог
float maxLength = 0;
float minLength = Math.max(Numbers.mapHeight, Numbers.mapWidth);
for (IRoad __r : roads){
Road _r = (Road)__r;
float l = 0;
float x1 = _r.getFCross().getCoord().getX();
float x2 = _r.getLCross().getCoord().getX();
float y1 = _r.getFCross().getCoord().getY();
float y2 = _r.getLCross().getCoord().getY();
l = (float) Math.sqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1));
_r.setLength(l);
if (l>maxLength){
maxLength = l;
}
if (l<minLength){
minLength = l ;
}
}
retVal.setMaxSpeed(maxLength / Numbers.HOUR_IN_SECONDS ) ;
retVal.setMinDist(minLength / Numbers.AUTO_SHORROAD_COUNT);
retVal.setCrossBuffer(retVal.getMinDist() * Numbers.CROSS_BUFFER_PERCENT);
//и макс скорость и мин дистанцию
retVal.setL_crossRoads(l_crossRoads);
retVal.setA_crossRoads(a_crossRoads);
retVal.setRoads((ArrayList<IRoad>)roads);
return retVal;
} //makeRoadMap
private boolean isCorrect(ArrayList<ICrossRoad> crosses, ArrayList<IRoad> roads, int tryCount) throws Exception {
if (tryCount<0)
throw new Exception("не удалось получить планарный связный граф");
//java.util.
HashSet<CrossRoad> register = new HashSet<CrossRoad>();
this.checkBfs(crosses, register);
if (register.size() != crosses.size()){
return false;
}
for (IRoad r1 :roads){
for (IRoad r2 : roads){
if (r1.equals(r2)){
continue;
}
if (this.roadsCross(r1,r2))
return false;
}
}
return true;
}
private void checkBfs(ArrayList<ICrossRoad> crosses, HashSet<CrossRoad> register) throws Exception {
CrossRoad cr = (CrossRoad) crosses.get(0);
if (cr != null){
register.add(cr);
_checkBfs(cr, register);
}
}
private void _checkBfs(CrossRoad cr, HashSet<CrossRoad> register) throws Exception {
for (IRoad r : cr.getRoads()){
if (r != null){
CrossRoad _cr = (CrossRoad)((Road) r).getOtherEnd((ICrossRoad)cr);
if (!register.contains(_cr)){
register.add(_cr);
this._checkBfs(_cr, register);
}
}
}
}
private boolean roadsCross(IRoad r1, IRoad r2) {
return false;
/* Point a = r1.getFCross().getCoord();
Point b = r1.getLCross().getCoord();
Point c = r2.getFCross().getCoord();
Point d = r2.getLCross().getCoord();
return _roadsCross (a.getX(), b.getX(), c.getX(), d.getX())
&& _roadsCross (a.getY(), b.getY(), c.getY(), d.getY())
&& area(a,b,c) * area(a,b,d) <= 0
&& area(c,d,a) * area(c,d,b) <= 0; */
}
private float area(Point a, Point b, Point c) {
return Math.signum((b.getX() - a.getX()) * (c.getY() - a.getY()) - (b.getY() - a.getY()) * (c.getX() - a.getX()));
}
private boolean _roadsCross(float a, float b , float c , float d) {
float t = a;
if (a > b){
a = b;
b = t;
}
t = c ;
if (c > d){
c = d;
d = t;
}
return Math.max(a,c) <= Math.min(b,d);
}
private void removeNegatives(ArrayList<ICrossRoad> l_crossRoads) {
for (ICrossRoad _cr : l_crossRoads){
CrossRoad cr = (CrossRoad) _cr;
Point p = cr.getCoord();
float d = 0 ;
if (p.getX()<0){
// p.setX(Math.abs(p.getX()));
p.setX(0);
}
if (p.getY()<0){
//p.setY(Math.abs(p.getY()));
p.setY(0);
}
if (p.getX()>Numbers.mapWidth){
//p.setX(2*Numbers.mapWidth - p.getX());
p.setX(0);
}
if (p.getY()>Numbers.mapWidth){
//p.setY((2*Numbers.mapHeight-p.getY()));
p.setY(0);
}
cr.setCoord(p);
}
}
private IRoad createConnector(ICrossRoad c0, ICrossRoad c1) {
Road retVal = new Road(c0.getMap());
if (c0.getCoord().getY()<c1.getCoord().getY()){
retVal.setFCross(c0);
retVal.setLCross(c1);
}else {
if (c0.getCoord().getX()<c1.getCoord().getX()){
retVal.setFCross(c0);
retVal.setLCross(c1);
}else {
retVal.setFCross(c1);
retVal.setLCross(c0);
}
}
return retVal;
}
private float rand(){
return (float) (0.5f*Math.pow(-1, Math.round(Math.random())) * Math.random());
//return 0.0f;
}
}