Package wolf.city

Source Code of wolf.city.Roadmap

package wolf.city;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import org.apache.commons.configuration.AbstractFileConfiguration;
import org.apache.commons.configuration.Configuration;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.lwjgl.util.vector.Vector3f;

import com.vividsolutions.jts.algorithm.Angle;
import com.vividsolutions.jts.algorithm.CGAlgorithms;
import com.vividsolutions.jts.algorithm.LineIntersector;
import com.vividsolutions.jts.algorithm.RobustLineIntersector;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryCollection;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.LineSegment;
import com.vividsolutions.jts.geom.Point;
import com.vividsolutions.jts.index.quadtree.Quadtree;

import wolf.city.road.GridSpace;
import wolf.city.road.Intersection;
import wolf.city.road.Road;
import wolf.city.road.RoadGrid;
import wolf.city.road.RoadQueue;
import wolf.city.road.RoadType;
import wolf.city.road.rules.Basic;
import wolf.city.road.rules.Direction;
import wolf.city.road.rules.Grid;
import wolf.city.road.rules.OffRamp;
import wolf.city.road.rules.RoadRule;
import wolf.gui.CityView;
import wolf.util.Log;
import wolf.util.OBJ;
import wolf.util.OBJOutput;
import wolf.util.RandomHelper;
import wolf.util.Turtle;

public class Roadmap implements OBJOutput{
 
  public Quadtree roads;
  private City city;
  private Configuration config;
  private LineIntersector li;
  private Log log;

  private static final float roadThickness = 5;
  private static final boolean roadToZero = true; //exported .obj file's roads will go to the base, or just have a thickness
  private float minimumPopulationHighwayIntersection; //load all parameters from generation properties file
  private int populationSampleRadiusHighwayIntersection;
  private int noWaterSampleRadius;
  public float noWaterCutoffDensity;
  private int bridgeMaxLength;
  private int bridgeTests;
  private int bridgePopulationCheckRadius;
  private float bridgeMinimumPopulation;
  private int intersectionMaxConnections;
  private double seedHighwayAngleSize;
  private float minimumPopulation;
  private boolean seedAtCenter;
  //private int highwayCount;
  private float minimumPopulationMainRoad;
  private int populationSampleRadiusMainRoad;
  private int popTests;
  private int waterTests;
  private double maximumRatioIntersectionArea;
  private double minimumIntersectionAngle;
  private double minimumRoadLength;
  private double maximumRoadSnapDistance;
  @SuppressWarnings("unused")
  private double minimumRoadSnapDistance;
  public double chaos;
  //private float minimumPopulationHighway;
  public boolean finished = false;
  public Geometry shape; //generate when final
  private int minimumNumberParents;
 
  public CityView cv;
 
  private RoadQueue rqH; //highways
  private RoadQueue rqM; //main roads
  private RoadQueue rq; //streets
  private GeometryFactory gf = new GeometryFactory();


  public Roadmap(City city){
    log = city.log;
    this.city = city;
    roads = new Quadtree();
    //Load configuration file
    try {
      File configFile = new File("config/roadmapConfig.properties");
      File parent = configFile.getParentFile();
      if(!parent.exists() && !parent.mkdirs()){
        //bad
      }
      if(!configFile.exists()){
        configFile.createNewFile();
      }
      config = new PropertiesConfiguration(configFile);
    } catch (ConfigurationException e) {

      e.printStackTrace();
    } catch (IOException e) {
      e.printStackTrace();
    }
    minimumPopulationHighwayIntersection = config.getFloat("minimumPopulationHighwayIntersection", .3f); //load all parameters from generation properties file
    populationSampleRadiusHighwayIntersection = config.getInt("populationSampleRadiusHighwayIntersection", 5);
    noWaterSampleRadius = config.getInt("noWaterSampleRadius", 3);
    noWaterCutoffDensity = config.getFloat("noWaterCutoffDensity", .8f);
    bridgeMaxLength = config.getInt("bridgeMaxLength", 512);
    bridgeTests = config.getInt("bridgeTests", 32);
    bridgePopulationCheckRadius = config.getInt("bridgePopulationCheckRadius", 5);
    bridgeMinimumPopulation = config.getFloat("bridgeMinimumPopulation", .4f);
    intersectionMaxConnections = config.getInt("intersectionMaxConnections", 5);
    seedHighwayAngleSize = config.getDouble("seedHighwayAngleSize", 15);
    minimumPopulation = config.getFloat("minimumPopulation", .17f);
    //minimumPopulationHighway = config.getFloat("minimumPopulationHighway", .3f);
    seedAtCenter = config.getBoolean("seedAtCenter", false);
    minimumPopulationMainRoad = config.getFloat("minimumPopulationMainRoad", .40f);
    populationSampleRadiusMainRoad = config.getInt("populationSampleRadiusMainRoad", 5);
    popTests = config.getInt("popTests", 8);
    waterTests = config.getInt("waterTests", 8);
    maximumRatioIntersectionArea = config.getDouble("maximumRatioIntersectionArea", .1);
    minimumIntersectionAngle = config.getDouble("minimumIntersectionAngle", 35d); //25
    minimumRoadLength = config.getDouble("minimumRoadLength", 5d);
    maximumRoadSnapDistance = config.getDouble("maximumRoadSnapDistance", 40d);
    minimumRoadSnapDistance = config.getDouble("minimumRoadSnapDistance", 2d);
    minimumNumberParents = config.getInt("minimumNumberParents", 8);
    chaos = config.getDouble("chaos", .5);

    try {
      ((AbstractFileConfiguration) config).save();
    } catch (ConfigurationException e) {
      e.printStackTrace();
    }

    li = new RobustLineIntersector();
  }

 
  public void setupGeneration(){
    log.log("Starting roadmap generation");
    rqH = new RoadQueue(); //highways
    rqM = new RoadQueue(); //main roads
    rq = new RoadQueue(); //streets

    //seed map with a couple highways
    seedRoadMap(rqH);

    //generate highways entirely

    //log.log("Highways generating");
  }
 
  public boolean generateIteration(CityView cv){
   
    if(!rqH.isEmpty()){
      //generate highways, save street seeds to rq
      Road road = rqH.remove();
      if(road.finished){
        //finished
      }else{
        if(city.pop.getCircleAvg((int)road.b.pos.x, (int)road.b.pos.y, populationSampleRadiusMainRoad) >= minimumPopulationMainRoad){
          //seed a main road (these go from highway to highway)
          Road r;
          if(city.random.nextBoolean()){
            r = road.rule.globalGoals(city, road, Direction.LEFT);
          }else{
            r = road.rule.globalGoals(city, road, Direction.RIGHT);
          }
          r.setType(RoadType.MAIN);
          rqM.add(localConstraints(r));

        }else if(city.pop.getCircleAvg((int)road.b.pos.x, (int)road.b.pos.y, populationSampleRadiusHighwayIntersection) >= minimumPopulationHighwayIntersection){
          //determine if this location should have an intersection (or should all locations have an intersection and then prune later?)

          OffRamp rampRule = new OffRamp(city);
          //yes, have an intersection! Free, with your purchase!
          rq.add(localConstraints(rampRule.globalGoals(city, road, Direction.LEFT)));
          rq.add(localConstraints(rampRule.globalGoals(city, road, Direction.RIGHT)));
        }
        if(road.getType() == RoadType.HIGHWAY){
          Basic basicRule = new Basic(city);
          Road newRoad = basicRule.globalGoals(city, road, Direction.FORWARD);
          rqH.add(localConstraints(newRoad));
        }else{
          rqM.add(road);
        }
       
      }
      road = connect(road);
      roads.insert(road.getEnvelope(), road);
      cv.roads.add(road);
      if(cv != null){
        cv.draw();
      }
    }


    //setup for main roads
    //basicRule.turnRateForward = 40;
    //log.log("Main roads generating");
    rqM.stackStyle = true;
    if(!rqM.isEmpty()){
      Road road = rqM.remove();
      if(road.finished){
        //finished
      }else{
        Road r = road.rule.globalGoals(city, road, Direction.FORWARD);
        r.setType(RoadType.MAIN);
        //have an intersection! Free, with your purchase!
        Road inters1 = r.rule.globalGoals(city, road, Direction.LEFT);
        Road inters2 = r.rule.globalGoals(city, road, Direction.RIGHT);
        //set the type to street, give it a proper rule
        inters1.setType(RoadType.STREET);
        inters1.rule = new Grid(city);
        inters2.setType(RoadType.STREET);
        inters2.rule = new Grid(city);

        rq.add(inters1);
        rq.add(inters2);
        rqM.add(localConstraints(r));
      }
     
      road = connect(road);
      roads.insert(road.getEnvelope(), road);
      cv.roads.add(road);
     
      if(cv != null){
        cv.draw();
      }
    }
    //generate streets entirely

    //log.log("Streets generating");
    rq.stackStyle = true;
    if(!rq.isEmpty()){
     
      //generate streets
      if(city.random.nextDouble()>.99){
        rq.stackStyle = false;
      }else{
        rq.stackStyle = true;
      }
      Road road = localConstraints(rq.remove());
      if(road != null){
        if(road.finished){
          //finished
        }else{
          if(road.rule instanceof OffRamp){
            road.rule = new Grid(city);
          }
          if(city.random.nextDouble()>.9){ //makes it look like a modern/whatever neighborhood
            road.rule = road.rule.mutate();
          }
          //use grid pattern to fill in areas between highways (Manhattan-esque pattern, but not perfect)
          if(city.pop.get((int)road.b.pos.x, (int)road.b.pos.y)>minimumPopulation){
            rq.add(localConstraints(road.rule.globalGoals(city, road, Direction.BACKWARD)));
            rq.add(localConstraints(road.rule.globalGoals(city, road, Direction.FORWARD)));
            rq.add(localConstraints(road.rule.globalGoals(city, road, Direction.LEFT)));
            rq.add(localConstraints(road.rule.globalGoals(city, road, Direction.RIGHT)));
          }
          //city.pop.removeDensityLine(road);
        }
        road = connect(road);
        roads.insert(road.getEnvelope(), road);
        cv.roads.add(road);

      }
      if(cv != null){
        cv.draw();
      }
    }
    if(rq.isEmpty() && rqH.isEmpty() && rqM.isEmpty()){
      return false;
    }else return true;
  }
 
  public void generate(){
    setupGeneration();

    while(generateIteration(cv));
   
    //    {//trim roads with not enough 'parents'
    //      for(int i=0; i<roads.size(); i++){
    //        if(roads.get(i).numberParents<minimumNumberParents){
    //          roads.remove(i);
    //        }
    //      }
    //    }
    //intersection fix
//    for(int i=0; i<roads.size(); i++){
//      Road a = roads.get(i);
//      for(int j=0; j<roads.size(); j++){
//        Road b = roads.get(j);
//        LineSegment extendedLine = a.getLineSegment();
//        double length = extendedLine.getLength();
//        extendedLine.pointAlong(length*1.1);
//        Coordinate c = extendedLine.intersection(b.getLineSegment()); //problem is that if they touch at all, it will intersect
//        if(c != null && c.distance(a.a.pos)>floatingPointError && c.distance(a.b.pos)>floatingPointError && c.distance(b.a.pos)>floatingPointError && c.distance(b.b.pos)>floatingPointError){
//          {
//            Intersection end = a.b;
//            Intersection mid = new Intersection(c);
//            a.b = mid;
//            Road r = new Road(a);
//            r.a = mid;
//            r.b = end;
//            roads.add(r);
//          }
//          {
//            Intersection end = b.b;
//            Intersection mid = new Intersection(c);
//            b.b = mid;
//            Road r = new Road(b);
//            r.a = mid;
//            r.b = end;
//            roads.add(r);
//          }
//        }
//      }
//      if(cv != null){
//        cv.draw();
//      }
//    }
    log.log("Roads: "+roads.size());
    {//union all of the road geometries
      Geometry[] geoms = new Geometry[roads.size()];
      ArrayList<Road> roadList = (ArrayList<Road>) roads.queryAll();
      for(int i=0; i<roadList.size(); i++){
        geoms[i] = (roadList.get(i).getFinalGeometry());
      }
      GeometryFactory gf = new GeometryFactory();
      GeometryCollection polygonCollection = gf.createGeometryCollection(geoms);
      //union
      shape = polygonCollection.buffer(0);
    }
    finished = true;
  }


  private Road connect(Road road) {
    if(road.intersectedRoad != null){
      Road split = new Road(road.b, road.intersectedRoad.b, road.intersectedRoad.getType(), road.intersectedRoad.rule);
      road.intersectedRoad.b = road.b;
      split.a.addConnecting(road);
      roads.insert(split.getEnvelope(), split);
      cv.roads.add(split);
    }
    road.a.addConnecting(road); //connect intersections
    road.b.addConnecting(road);
    return road;
  }


  private void seedRoadMap(RoadQueue roadQueue) {
    Basic basicRule = new Basic(city);
    //seed at center (not such a great thing)
    if(seedAtCenter){
      float x, y;
      while(true){ //keep these variables in check
        x = (float) ((this.city.random.nextFloat()-.5)*city.sizeX); //can place in center half of city
        y = (float) ((this.city.random.nextFloat()-.5)*city.sizeY);
        /*if(city.water.getCircleAvg((int)x, (int)y, noWaterSampleRadius) < noWaterCutoffDensity){
          break;
        }*/
        break;
      }
      Coordinate startPoint = new Coordinate(x,y);
      double angle = Math.toDegrees(Angle.angle(startPoint, new Coordinate(0,0)));
      Turtle t = new Turtle(startPoint, angle);
      //t.turn(45);
      t.move(32);
      roadQueue.add(new Road(new Intersection(startPoint), new Intersection(t.pos), RoadType.HIGHWAY, basicRule));
    }
    int highwayCount = Math.max((city.sizeX*2+city.sizeY*2)/(3*1024),1);
    for(int i=0; i<highwayCount; i++){
      //highway from random side
      int direction = Math.abs(city.random.nextInt()%4);
      float length = 128;
      switch(direction){
      case 0:{ //North
        log.log("Highway from the north");
        float x = (float) ((this.city.random.nextFloat()-.5)*city.sizeX); //can place in center half of city
        Coordinate startPoint = new Coordinate(x, city.sizeY/2);
        Turtle t = new Turtle(startPoint, ((city.random.nextDouble()*180)%seedHighwayAngleSize)+270);
        t.move(length);
        roadQueue.add(new Road(new Intersection(startPoint), new Intersection(t.pos), RoadType.HIGHWAY, basicRule));
        break;
      }
      case 1:{ //South
        log.log("Highway from the south");
        float x = (float) ((this.city.random.nextFloat()-.5)*city.sizeX); //can place in center half of city
        Coordinate startPoint = new Coordinate(x, -city.sizeY/2);
        Turtle t = new Turtle(startPoint, ((city.random.nextDouble()*180)%seedHighwayAngleSize)+90);
        t.move(length);
        roadQueue.add(new Road(new Intersection(startPoint), new Intersection(t.pos), RoadType.HIGHWAY, basicRule));
        break;
      }
      case 2:{ //East
        log.log("Highway from the east");
        float y = (float) ((this.city.random.nextFloat()-.5)*city.sizeY);
        Coordinate startPoint = new Coordinate(-city.sizeX/2, y);
        Turtle t = new Turtle(startPoint, ((city.random.nextDouble()*180)%seedHighwayAngleSize)+0);
        t.move(length);
        roadQueue.add(new Road(new Intersection(startPoint), new Intersection(t.pos), RoadType.HIGHWAY, basicRule));
        break;
      }
      case 3:{ //West
        log.log("Highway from the west");
        float y = (float) ((this.city.random.nextFloat()-.5)*city.sizeY);
        Coordinate startPoint = new Coordinate(city.sizeX/2, y);
        Turtle t = new Turtle(startPoint, ((city.random.nextDouble()*180)%seedHighwayAngleSize)+180);
        t.move(length);
        roadQueue.add(new Road(new Intersection(startPoint), new Intersection(t.pos), RoadType.HIGHWAY, basicRule));
        break;
      }
      default:{
        System.out.println("You done goofed. Check the seedRoadMap function's direction variable");
      }
      }
    }

  }

  public Road localConstraints(Road r){
    //cheap tests
    r = lengthCheck(r);
    r = maxConnections(r);
    r = inBounds(r);

    r = snapToIntersection(r);
    r = intersectionAngleCheck(r);
    //expensive tests
    //r = waterCheck(r);
    r = proximityCheck(r);
    r = trimToIntersection(r);
    r = lengthCheck(r); //fixes from trim
    r = popCheck(r);
    return r;
  }





  private Road snapToIntersection(Road r) {
    if(r==null){
      return null;
    }

    Coordinate point =  r.b.pos;
    ArrayList<Road> roadsLoc = (ArrayList<Road>) roads.query(r.getEnvelope());
    for(int ir=0; ir<roadsLoc.size(); ir++){
      Road i = roadsLoc.get(ir);

      if(i.getType() == RoadType.HIGHWAY && r.getType() == RoadType.STREET){

      }else{
        //doesn't find closest, just finds one and goes with it.
        double distA = r.b.pos.distance(i.a.pos);
        double distB = r.b.pos.distance(i.b.pos);
        if(distA<maximumRoadSnapDistance && distB<maximumRoadSnapDistance){
          if(distA<distB){
            r.b = i.a;
            return r;
          }else{
            r.b = i.b;
            return r;
          }
        }else{
          if(distA<maximumRoadSnapDistance){
            r.b = i.a;
            return r;
          }
          if(distB<maximumRoadSnapDistance){
            r.b = i.b;
            return r;
          }
        }

        //snap to road
        Coordinate closest = i.getLineSegment().closestPoint(point);
        double dist = point.distance(closest);
        if(dist < maximumRoadSnapDistance ){//&& dist > minimumRoadSnapDistance){
          r.b.pos = closest;
          r.intersectedRoad = i;
          return r;
        }
      }
    }

    return r;
  }


  private Road intersectionAngleCheck(Road r) {
    if(r==null){
      return null;
    }

    Geometry g0 = r.getGeometry(2).difference(r.getIntersectionGeometry());
    double angle = Angle.angle(r.a.pos, r.b.pos);
    //double dist = (Math.pow(r.a.pos.x,2)+Math.pow(r.a.pos.y,2));
    //double normX = r.a.pos.x/dist;
    //double normY = r.a.pos.y/dist;
    //double angle = Math.atan2(r.a.pos.x, r.a.pos.y);

    if(angle<0){
      angle = Math.PI + angle; //half circle
    }

    ArrayList<Road> roadList = (ArrayList<Road>) roads.query(r.getEnvelope());
    for(int i=0; i<roadList.size(); i++){
      Road road = roadList.get(i);
      Geometry g1 = road.getGeometry(2).difference(road.getIntersectionGeometry());
      if(g0.intersects(g1) || g0.distance(g1)<4){
        double thisAngle = Angle.angle(road.a.pos, road.b.pos);
        if(thisAngle<0){
          thisAngle = Math.PI + thisAngle; //half circle
        }
        double bigAngle = Math.max(thisAngle, angle);
        double smallAngle = Math.min(thisAngle, angle);

        double difference = Math.toDegrees(bigAngle - smallAngle);

        if(difference<minimumIntersectionAngle || difference>(360-minimumIntersectionAngle)){
          return null;
        }
      }
    }


    return r;
  }

  private Road proximityCheck(Road r) {
    if(r==null){
      return null;
    }
    double expand = r.width*1f;
    Geometry a = r.getGeometry(expand);
    //check related spaces in grid
    ArrayList<Road> roadList = (ArrayList<Road>) roads.query(r.getEnvelope());
    for(int i=0; i<roadList.size(); i++){
      Road ir = roadList.get(i);
      if(ir.getType() != RoadType.HIGHWAY && ir.getType() != RoadType.MAIN){
        //if(!tested.contains(i) && !((i.getType() == RoadType.HIGHWAY || i.getType() == RoadType.MAIN) && (r.getType() == RoadType.STREET || r.getType() == RoadType.HIGHWAY)) /*streets ignore main and highway types*/){
        Geometry b = ir.getGeometry(expand);
        if(a.intersects(b)){
          Geometry c = a.intersection(b);
          if(c.getArea()>maximumRatioIntersectionArea*a.getArea()){
            return null;
          }
          if(c.getArea()>maximumRatioIntersectionArea*b.getArea()){
            return null;
          }
        }
      }
    }
    return r;
  }


  private Road lengthCheck(Road r) {
    if(r==null){
      return null;
    }
    double distance = r.a.pos.distance(r.b.pos);

    if(distance>r.width*1.5){ //minimum road length
      return r;
    }

    return null;
  }


  private Road inBounds(Road r) {
    if(r==null){
      return null;
    }

    boolean aInside = (r.a.pos.x > -city.sizeX/2 && r.a.pos.x <= city.sizeX/2 && r.a.pos.y > -city.sizeY/2 && r.a.pos.y <= city.sizeY/2);
    boolean bInside = (r.b.pos.x > -city.sizeX/2 && r.b.pos.x <= city.sizeX/2 && r.b.pos.y > -city.sizeY/2 && r.b.pos.y <= city.sizeY/2);
    if(!aInside || !bInside){
      r.finished = true;
      return r;
    }

    return r;
  }


  private Road trimToIntersection(Road r) {
    if(r==null){
      return null;
    }

    if(r.getType() == RoadType.MAIN || r.getType() == RoadType.HIGHWAY){
      ArrayList<Road> roadList = (ArrayList<Road>) roads.query(r.getEnvelope());
      for(int i=0; i<roadList.size(); i++){
        Road road = roadList.get(i);

        li.computeIntersection(r.a.pos, r.b.pos, road.a.pos, road.b.pos);
        if(li.hasIntersection()){
          Coordinate intersection = li.getIntersection(0);
          r.intersectedRoad = road;
          r.b.pos = intersection;
        }
      }
    }
    //      if(splitRoad != null){
    //        Intersection b = splitRoad.b;
    //        splitRoad.b = new Intersection(r.b.pos);
    //        //splitRoad.b.addConnecting(r); //may not be best idea
    //        Road split = new Road(splitRoad.b, b, splitRoad.getType(), splitRoad.rule);
    //        roads.add(connect(split));
    //        grid.add(split); //for collision detection
    //      }
    return r;
  }

  private Road popCheck(Road r) {
    if(r==null){
      return null;
    }
    for(int i=0; i<popTests; i++){
      double xD = r.a.pos.x - r.b.pos.x;
      double yD = r.a.pos.y - r.b.pos.y;

      double x = (xD/(popTests+(popTests/10d)))*(i+(popTests/10d));
      double y = (yD/(popTests+(popTests/10)))*(i+(popTests/10));
      if(r.getType() == RoadType.HIGHWAY){
        //if(city.pop.get((int)x, (int)y)<=minimumPopulationHighway){
        //  r.setType(RoadType.MAIN); //no longer a highway, but a main road still;
        //}
      }
      if(city.pop.get((int)(x+r.b.pos.x), (int)(y+r.b.pos.y))<=minimumPopulation){
        if(r.getType() != RoadType.HIGHWAY){
          return null;
        }else{
          if(city.pop.get((int)x, (int)y)<=0){ //highways should be able to leave city, but end on edge
            return null;
          }
        }
      }
    }

    return r;
  }

  private Road maxConnections(Road r) {
    if(r==null){
      return null;
    }
    if(r.a.connecting.size()>intersectionMaxConnections){
      r.a.connecting.remove(r);
      r = null;
      return null;
    }
    return r;
  }

  @Override
  public void asOBJ(OBJ obj) {
    obj.startObject("roads");
   
    for(int i=0; i<Intersection.intersections.size(); i++){
      Intersection is = Intersection.intersections.get(i);
      if(is.connecting.size() > 1){ //if there is more than one road to make an intersection out of
        ArrayList<LineSegment> segments0 = new ArrayList<LineSegment>();
        ArrayList<LineSegment> segments1 = new ArrayList<LineSegment>();
        //compute intersection points and max radius of roads (minimum value for roadExtrusion value)
        double maxRadius = 0;
        for(int j=0; j<is.connecting.size(); j++){
          Road r = is.connecting.get(j);
          double radius = r.width/2;
          if(radius>maxRadius){
            maxRadius = radius;
          }
          //calculate intersection lines
          Turtle t0;
          if(r.a.pos.equals(is.pos)){
            t0 = new Turtle(is.pos, Math.toDegrees(Angle.angle(is.pos, r.b.pos)));
          }else{
            t0 = new Turtle(is.pos, Math.toDegrees(Angle.angle(is.pos, r.a.pos)));
          }
          Turtle t1 = new Turtle(t0.pos, t0.angle);
          //move out to sides of roads
          t0.turn(90);
          t1.turn(-90);
          t0.move(radius);
          t1.move(radius);
          Coordinate t0Start = new Coordinate(t0.pos);
          Coordinate t1Start = new Coordinate(t1.pos);
          //return to first orientation
          t0.turn(-90);
          t1.turn(90);
          t0.move(r.width*6);
          t1.move(r.width*6);
         
          LineSegment ls0 = new LineSegment(t0Start, t0.pos);
          LineSegment ls1 = new LineSegment(t1Start, t1.pos);
          segments0.add(ls0);
          segments1.add(ls1);
        }
        //compute intersections of roads extruded from center of intersection.
        double[] extrusion = new double[is.connecting.size()];
        //double maxDistance = -1;
        for(int j=0; j<is.connecting.size(); j++){
          for(int k=0; k<is.connecting.size(); k++){
            if(j != k){
              Coordinate c0 = segments0.get(j).intersection(segments1.get(k));
              Coordinate c1 = segments1.get(j).intersection(segments0.get(k));
              if(c0 != null){
                double dist = c0.distance(is.pos);
               
                if(dist>extrusion[j]){
                  extrusion[j] = dist;
                  log.log("dist: "+dist+" other");
                }
              }
             
              if(c1 != null){
                double dist = c1.distance(is.pos);
               
                if(dist>extrusion[j]){
                  extrusion[j] = dist;
                }
              }
             
            }
          }
          if(extrusion[j] < maxRadius) extrusion[j] = maxRadius;
          for(int k=0; k<is.connecting.size(); k++){
            if(is.connecting.get(k).a == is){
              is.connecting.get(k).roadExtrusionA = extrusion[k];
            }else{
              is.connecting.get(k).roadExtrusionB = extrusion[k];
            }
          }
        }
        //create final road extrusion
       
        LineSegment[] segments = new LineSegment[is.connecting.size()];
        for(int j=0; j<is.connecting.size(); j++){
          Road r = is.connecting.get(j);
          double radius = r.width/2; //maybe a tad faster to load into variable?
          Turtle t0;
          if(r.a.pos.equals(is.pos)){
            t0 = new Turtle(is.pos, Math.toDegrees(Angle.angle(is.pos, r.b.pos)));
          }else{
            t0 = new Turtle(is.pos, Math.toDegrees(Angle.angle(is.pos, r.a.pos)));
          }
         
          //move out from intersection and split into road width
          t0.move(extrusion[j]);
          t0.turn(90);
          Turtle t1 = new Turtle(t0.pos, t0.angle-180);
          t0.move(radius);
          t1.move(radius);
         
          segments[j] = new LineSegment(t0.pos, t1.pos);
        }
        //need to sort segments by angle order, then use that to generate shape. Convex hull will not work.
       
        double[] angles = new double[segments.length]; //no ring, so no last element
        //calculate all point angles
        for(int j=0; j<angles.length; j++) angles[j] = Math.min(Math.PI*2-Angle.angle(is.pos, segments[j].p0),Math.PI*2-Angle.angle(is.pos, segments[j].p1));
        //selection sort
        for(int j=0; j<angles.length; j++){
          int min = j;
          for(int k=j+1; k<angles.length; k++){
            if(angles[k]<angles[min]){
              min = k; //remember new min
            }
          }
         
          //swap min element with current element
          if(min != j){
            //swap angle array
            double ang = angles[j];
            angles[j] = angles[min];
            angles[min] = ang;
           
            //swap segment array
            LineSegment tempS = segments[j];
            segments[j] = segments[min];
            segments[min] = tempS;
          }
        }
        //close ring
        Coordinate[] points = new Coordinate[is.connecting.size()*2+1];
        for(int j=0; j<segments.length; j++){
          points[j*2] = segments[j].p0;
          points[j*2+1] = segments[j].p1;
        }
        points[points.length-1] = points[0];
        //create ring
        Geometry intersectionShape = gf.createLinearRing(points);
        obj.startObject("intersection_"+this.hashCode());
 
        //convert Coordinate to Vector3f
        Coordinate[] cs = intersectionShape.getCoordinates();
        Vector3f[] verts = new Vector3f[cs.length];
        Vector3f[] vertsz = new Vector3f[cs.length];
 
       
       
       
        float elevation = city.ter.get((int)is.pos.x, (int)is.pos.y);
 
        for(int j=0; j<cs.length; j++){
          verts[cs.length-j-1] = new Vector3f((float)cs[j].x,(float)cs[j].y, elevation);
          vertsz[j] = new Vector3f((float)cs[j].x,(float)cs[j].y, 0);
        }
 
        for(int j=0; j<cs.length; j++){
          obj.face(new Vector3f[]{new Vector3f(verts[j].x, verts[j].y, 0), new Vector3f(verts[(j+1)%cs.length].x, verts[(j+1)%cs.length].y, 0), verts[j]});
          obj.face(new Vector3f[]{new Vector3f(verts[(j+1)%cs.length].x, verts[(j+1)%cs.length].y, 0), verts[(j+1)%cs.length], verts[j]});
        }
 
        obj.face(verts);
        obj.face(vertsz);
        obj.endObject();
      }else{
        //roads that have only 1 intersection
       
      }
    }
    ArrayList<Road> roadList = (ArrayList<Road>) roads.queryAll();
    for(int i=0; i<roadList.size(); i++){
      Road r = roadList.get(i);
      //make rectangle geometry with road ends as terrain height.
      Vector3f a = new Vector3f((float)r.a.pos.x, (float)r.a.pos.y, city.ter.get((int)r.a.pos.x, (int)r.a.pos.y));
      Vector3f b = new Vector3f((float)r.b.pos.x, (float)r.b.pos.y, city.ter.get((int)r.b.pos.x, (int)r.b.pos.y));

      double ang = Angle.angle(r.a.pos, r.b.pos);
     
      //for avoiding intersection center ... doesn't work flawlessly
      float xBackA = (float)(Math.cos(ang)*(r.roadExtrusionA));
      float xBackB = (float)(Math.cos(ang)*(r.roadExtrusionB));
      float yBackA = (float)(Math.sin(ang)*(r.roadExtrusionA));
      float yBackB = (float)(Math.sin(ang)*(r.roadExtrusionB));
     
      a.x += xBackA;
      a.y += yBackA;
      b.x -= xBackB;
      b.y -= yBackB;
     
      float x = (float)Math.cos(ang+Math.toRadians(90))*(r.width/2);
      float y = (float)Math.sin(ang+Math.toRadians(90))*(r.width/2);

      Vector3f p1 = new Vector3f(a.x+x, a.y+y, a.z);
      Vector3f p2 = new Vector3f(b.x+x, b.y+y, b.z);

      Vector3f p3 = new Vector3f(b.x-x, b.y-y, b.z);
      Vector3f p4 = new Vector3f(a.x-x, a.y-y, a.z);
     
      Vector3f p1z, p2z, p3z, p4z;
     
      if(roadToZero){
        p1z = new Vector3f(a.x+x, a.y+y, 0);
        p2z = new Vector3f(b.x+x, b.y+y, 0);
        p3z = new Vector3f(b.x-x, b.y-y, 0);
        p4z = new Vector3f(a.x-x, a.y-y, 0);
      }else{
        p1z = new Vector3f(a.x+x, a.y+y, a.z-roadThickness);
        p2z = new Vector3f(b.x+x, b.y+y, b.z-roadThickness);
        p3z = new Vector3f(b.x-x, b.y-y, b.z-roadThickness);
        p4z = new Vector3f(a.x-x, a.y-y, a.z-roadThickness);
      }
     
      //top
      obj.face(new Vector3f[]{p4,p3,p2,p1});
     
      //sides
      //side
      obj.face(new Vector3f[]{p4z,p3z,p3,p4});
      //side
      obj.face(new Vector3f[]{p2z,p1z,p1,p2});
     
      //intersection (b)
      if(r.b.connecting.size()==1)
      obj.face(new Vector3f[]{p3z,p2z,p2,p3});
      //intersection (a)
      if(r.a.connecting.size()==1)
      obj.face(new Vector3f[]{p1z,p4z,p4,p1});
     
      //bottom
      obj.face(new Vector3f[]{p1z,p2z,p3z,p4z});
     
    }
    obj.endObject();
  }

  /*private Road waterCheck(Road r){
    if(r==null){
      return null;
    }
    Coordinate loc = r.b.pos;
    //WATER CHECK
    //is it in water?
    for(int test=0; test<waterTests; test++){
      double xD = r.a.pos.x - r.b.pos.x;
      double yD = r.a.pos.y - r.b.pos.y;

      double x = ((xD/(double)waterTests)*(double)test)+r.a.pos.x;
      double y = ((yD/(double)waterTests)*(double)test)+r.a.pos.y;
      float waterAvg = city.water.getCircleAvg((int)x, (int)y, noWaterSampleRadius);
      if(waterAvg >= noWaterCutoffDensity){
        //BRIDGE
        if(!(r.getType() == RoadType.HIGHWAY || r.getType() == RoadType.BRIDGE)){
          return null;
        }else{
          //technically, it should never be a bridge
          //make a bridge if high enough population density
          float population = city.pop.getCircleAvg((int)loc.x, (int)loc.y, bridgePopulationCheckRadius);

          if(population>=bridgeMinimumPopulation){
            double angle = Math.toDegrees(Angle.angle(r.a.pos,r.b.pos));
            Turtle t = new Turtle(r.a.pos, angle);
            boolean didSucceed = false;


            float minBridgeLength = bridgeMaxLength;
            float bridgeCurLength = bridgeMaxLength+1;
            double bestAngle = 0;
            Coordinate bestBridgePosition = null;
            for(int i=0; i<bridgeTests; i++){
              didSucceed = false;
              float angleMod = RandomHelper.random(city.random.nextFloat(), -30, 30);
              t = new Turtle(r.a.pos, angle+angleMod);
              float curLength = (float) r.a.pos.distance(r.b.pos);
              t.move(curLength);

              for(int j=0;j<bridgeTests;j++){
                t.move((bridgeMaxLength-curLength)/bridgeTests);
                float waterNewAvg = city.water.getCircleAvg((int)t.pos.x, (int)t.pos.y, noWaterSampleRadius);
                //System.out.println("water density: "+waterNewAvg);
                if(waterNewAvg < noWaterCutoffDensity){ //bridge can be built
                  didSucceed = true;
                  bridgeCurLength = (float) r.a.pos.distance(t.pos);
                  //System.out.println("succeeded!");
                  break;
                }

              }
              if(didSucceed && bridgeCurLength<minBridgeLength){
                minBridgeLength = bridgeCurLength;
                //System.out.println(bridgeCurLength);
                bestBridgePosition = t.pos;
                bestAngle = t.angle;
              }
            }
            if(didSucceed){
              boolean waterSeen = false;
              Turtle tt = new Turtle(r.a.pos, bestAngle);
              for(int j=0;j<bridgeTests;j++){
                tt.move(minBridgeLength/bridgeTests);
                float water = city.water.get((int)tt.pos.x, (int)tt.pos.y);
                if(water >= noWaterCutoffDensity){
                  waterSeen = true;

                }
              }
              if(waterSeen){
                r.setType(RoadType.BRIDGE);
              }else{
                r.setType(RoadType.HIGHWAY);
              }
              r.b.pos = bestBridgePosition;
              return r;
            }else{
              return null;
            }

          }

        }
      }
    }
    //Road passed WATER CHECK
    return r;
  }*/
}
 
TOP

Related Classes of wolf.city.Roadmap

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.