Package org.osm2world.core.world.creation

Source Code of org.osm2world.core.world.creation.NetworkCalculator

package org.osm2world.core.world.creation;

import java.util.ArrayList;
import java.util.List;

import org.osm2world.core.map_data.data.MapData;
import org.osm2world.core.map_data.data.MapElement;
import org.osm2world.core.map_data.data.MapNode;
import org.osm2world.core.map_data.data.MapSegment;
import org.osm2world.core.map_data.data.MapWaySegment;
import org.osm2world.core.math.VectorXZ;
import org.osm2world.core.world.data.NodeWorldObject;
import org.osm2world.core.world.network.JunctionNodeWorldObject;
import org.osm2world.core.world.network.NetworkWaySegmentWorldObject;
import org.osm2world.core.world.network.VisibleConnectorNodeWorldObject;

/**
* class that will calculate the information for those {@link MapElement}s
* that implement an interface from org.o3dw.representation.network and set it.
* Calling this is necessary for those representations to work properly!
*/
public class NetworkCalculator {
 
  private NetworkCalculator() {}
 
 
 
  //FIXME: must be able to handle connectors with lines of opposite direction!
 
 
 
  private static final float ROAD_PUSHING_STEP = 0.01f;
 
 
  /**
   * calculates cut and offset information for all
   * NetworkNode/Line/AreaRepresentations of elements in a grid.
   */
  public static void calculateNetworkInformationInGrid(MapData grid) {
   
    for (MapNode node : grid.getMapNodes()) {
     
     
      //TODO: also work with nodes that aren't Network*NodeRepresentations,
      //  but connect two NetworkWaySegmentRep.s (invisible connectors)
     
      final List<MapWaySegment> inboundNLines = new ArrayList<MapWaySegment>();
      List<MapWaySegment> outboundNLines = new ArrayList<MapWaySegment>();
     
      for (MapWaySegment line : node.getInboundLines()) {
        if (line.getPrimaryRepresentation() instanceof NetworkWaySegmentWorldObject) {
          inboundNLines.add(line);
        }
      }
     
      for (MapWaySegment line : node.getOutboundLines()) {
        if (line.getPrimaryRepresentation() instanceof NetworkWaySegmentWorldObject) {
          outboundNLines.add(line);
        }
      }

     
      if (node.getPrimaryRepresentation() instanceof JunctionNodeWorldObject) {

        /* junctions */
       
        calculateJunctionNodeEffects(node,
            (JunctionNodeWorldObject)node.getPrimaryRepresentation(),
            inboundNLines, outboundNLines);
       
      } else if (inboundNLines.size() + outboundNLines.size() == 2) {
       
        /* visible or invisible connectors */
       
        List<MapWaySegment> connectedNLines = new ArrayList<MapWaySegment>(2);
        connectedNLines.addAll(inboundNLines);
        connectedNLines.addAll(outboundNLines);
       
        MapWaySegment line1 = connectedNLines.get(0);
        MapWaySegment line2 = connectedNLines.get(1);
       
        calculateConnectorNodeEffects(node.getPrimaryRepresentation(),
            line1, line2,
            inboundNLines.contains(line1),
            inboundNLines.contains(line2));
       
      } else {
       
        for (MapWaySegment outboundNLine : outboundNLines) {
          setOrthogonalCutVector(outboundNLine, true);
        }

        for (MapWaySegment inboundNLine : inboundNLines) {
          setOrthogonalCutVector(inboundNLine, false);
        }
       
      }
     
    }
   
  }
 
  /**
   * calculates the effects of both visible and invisible connector nodes.
   */
  private static void calculateConnectorNodeEffects(
      NodeWorldObject nodeRepresentation,
      MapWaySegment line1, MapWaySegment line2,
      boolean inbound1, boolean inbound2) {
         
    NetworkWaySegmentWorldObject renderable1 =
      ((NetworkWaySegmentWorldObject)line1.getPrimaryRepresentation());
    NetworkWaySegmentWorldObject renderable2 =
      ((NetworkWaySegmentWorldObject)line2.getPrimaryRepresentation());
   
    VisibleConnectorNodeWorldObject visibleConnectorRep = null;
   
    if (nodeRepresentation instanceof VisibleConnectorNodeWorldObject) {
      visibleConnectorRep =
        (VisibleConnectorNodeWorldObject)nodeRepresentation;
    }
   
    /* calculate cut as angle bisector between the two lines */
   
    VectorXZ inVector = line1.getDirection();
    VectorXZ outVector = line2.getDirection();

    if (!inbound1) { inVector = inVector.invert(); }
    if (inbound2) { outVector = outVector.invert(); }
   
    VectorXZ cutVector;
   
    if (inVector.equals(outVector)) { //TODO: allow for some small difference?
      cutVector = outVector.rightNormal();
    } else {
      cutVector = outVector.subtract(inVector);
      cutVector = cutVector.normalize();
    }
   
    //make sure that cutVector points to the right, which is equivalent to:
    //y component of the cross product (inVector x cutVector) is positive.
    //If this isn't the case, invert the cut vector.
    if (inVector.z * cutVector.x - inVector.x * cutVector.z <= 0) {
      cutVector = cutVector.invert();
    }
   
    /* set calculated cut vector */
   
    if (inbound1) {
      renderable1.setEndCutVector(cutVector);
    } else {
      renderable1.setStartCutVector(cutVector.invert());
    }
   
    if (inbound2) {
      renderable2.setEndCutVector(cutVector.invert());
    } else {
      renderable2.setStartCutVector(cutVector);
    }
   
    /* perform calculations necessary for connectors
     * whose representation requires space */
   
    double connectorLength = 0;
   
    if (visibleConnectorRep != null) {
      connectorLength = visibleConnectorRep.getLength();
    }
   
    if (connectorLength > 0) {
                 
      /* move connected lines to make room for the node's representation */
     
      //connected node of line1 is moved orthogonally to the cut vector
      VectorXZ offset1 = cutVector.rightNormal();
      offset1 = offset1.mult(connectorLength / 2);
      if (inbound1) {
        renderable1.setEndOffset(offset1);
      } else {
        renderable1.setStartOffset(offset1);
      }
     
      //node of line2 is moved into the opposite direction
      VectorXZ offset2 = offset1.invert();
      if (inbound2) {
        renderable2.setEndOffset(offset2);
      } else {
        renderable2.setStartOffset(offset2);
      }
               
      /* provide information to node's representation */
     
      if (nodeRepresentation instanceof VisibleConnectorNodeWorldObject) {
       
        VisibleConnectorNodeWorldObject connectorRep =
          (VisibleConnectorNodeWorldObject)nodeRepresentation;
       
        VectorXZ connectedPos1;
        VectorXZ connectedPos2;
       
        if (inbound1) {
          connectedPos1 = line1.getEndNode().getPos();
        } else {
          connectedPos1 = line1.getStartNode().getPos();
        }
       
        if (inbound2) {
          connectedPos2 = line2.getEndNode().getPos();
        } else {
          connectedPos2 = line2.getStartNode().getPos();
        }
       
        connectorRep.setInformation(
            cutVector,
            connectedPos1.add(offset1),
            connectedPos2.add(offset2),
            renderable1.getWidth(),
            renderable2.getWidth());
       
      }
     
      //TODO: if done properly, this might affect NOT ONLY the directly adjacent lines
     
    }
   
  }

  private static void calculateJunctionNodeEffects(
      MapNode node, JunctionNodeWorldObject nodeRepresentation,
      final List<MapWaySegment> inboundNLines, List<MapWaySegment> outboundNLines) {
   
    /* create list of all connected roads.
     * Order of adds is important, it needs to match
     * the order of cutVectors, coords and widths adds. */
   
    List<MapWaySegment> connectedNSegments = new ArrayList<MapWaySegment>();
    connectedNSegments.addAll(inboundNLines);
    connectedNSegments.addAll(outboundNLines);
       
    //all cut vectors in here will point to the right from the junctions pov!
    List<VectorXZ> cutVectors = new ArrayList<VectorXZ>(connectedNSegments.size());
    List<VectorXZ> coords = new ArrayList<VectorXZ>(connectedNSegments.size());
    List<Float> widths = new ArrayList<Float>(connectedNSegments.size());
   
    /* determine cut angles:
     * always orthogonal to the connected line */
       
    for (MapWaySegment in : inboundNLines) {
     
      NetworkWaySegmentWorldObject inRenderable =
        ((NetworkWaySegmentWorldObject)in.getPrimaryRepresentation());
     
      VectorXZ cutVector = in.getRightNormal();
      inRenderable.setEndCutVector(cutVector);
      cutVectors.add(cutVector.invert());
     
      coords.add(in.getEndNode().getPos());
      widths.add(inRenderable.getWidth());
     
    }

    for (MapWaySegment out : outboundNLines) {
     
      NetworkWaySegmentWorldObject outRenderable =
        ((NetworkWaySegmentWorldObject)out.getPrimaryRepresentation());
     
      VectorXZ cutVector = out.getRightNormal();
      outRenderable.setStartCutVector(cutVector);
      cutVectors.add(cutVector);
     
      coords.add(out.getStartNode().getPos());
      widths.add(outRenderable.getWidth());
     
    }
   
    /* move roads away from the intersection until they cannot overlap anymore,
     * this is certain if the distance between their ends' center points
     * is greater than the sum of their half-widths */
   
    //TODO (performance) if roads were ordered by angle here already, this would be much faster -> only neighbors checked
   
    boolean overlapPossible;
   
    do {
     
      overlapPossible = false;
     
      overlapCheck:
      for (int r1=0; r1 < coords.size(); r1++) {
        for (int r2=r1+1; r2 < coords.size(); r2++) {
         
          /* ignore overlapping (or almost overlapping) way segments
           * as no reasonable amount of pushing would separate these */
          if (VectorXZ.distance(connectedNSegments.get(r1).getDirection(),
              connectedNSegments.get(r2).getDirection()) < 0.1
            ||  VectorXZ.distance(connectedNSegments.get(r1).getDirection(),
                connectedNSegments.get(r2).getDirection().invert()) < 0.1) {
            continue;
          }
         
          double distance = Math.abs(coords.get(r1).subtract(coords.get(r2)).length());
         
          if (distance > 200) {
            //TODO: proper error handling
            System.err.println("distance has exceeded 200 at node " + node
                + "\n (representation: " + nodeRepresentation + ")");
            // overlapCheck will remain false, no further size increase
            break overlapCheck;
          }
         
          if (distance <= widths.get(r1)*0.5 + widths.get(r2)*0.5) {
            overlapPossible = true;
            break overlapCheck;
          }
         
        }
      }
   
      if (overlapPossible) {
       
        /* push outwards */
       
        coords.clear();
       
        for (MapWaySegment in : inboundNLines) {
         
          NetworkWaySegmentWorldObject inRenderable =
            ((NetworkWaySegmentWorldObject)in.getPrimaryRepresentation());
          VectorXZ inVector = in.getDirection();
         
          VectorXZ offsetModification = inVector.mult(-ROAD_PUSHING_STEP);
         
          VectorXZ newEndOffset = inRenderable.getEndOffset().add(offsetModification);
          inRenderable.setEndOffset(newEndOffset);
          coords.add(in.getEndNode().getPos().add(newEndOffset));
         
        }

        for (MapWaySegment out : outboundNLines) {
         
          NetworkWaySegmentWorldObject outRenderable =
            ((NetworkWaySegmentWorldObject)out.getPrimaryRepresentation());
          VectorXZ outVector = out.getDirection();
         
          VectorXZ offsetModification = outVector.mult(ROAD_PUSHING_STEP);
         
          VectorXZ newStartOffset = outRenderable.getStartOffset().add(offsetModification);
          outRenderable.setStartOffset(newStartOffset);
          coords.add(out.getStartNode().getPos().add(newStartOffset));
         
        }
       
      }
     
    } while(overlapPossible);

    /* set calculated information using the correct order */
   
    List<MapSegment> segments = node.getConnectedSegments();
       
    ArrayList<VectorXZ> junctionCutCenters = new ArrayList<VectorXZ>(segments.size());
    ArrayList<VectorXZ> junctionCutVectors = new ArrayList<VectorXZ>(segments.size());
    ArrayList<Float> junctionWidths = new ArrayList<Float>(segments.size());
   
    for (MapSegment segment : segments) {
     
      if (connectedNSegments.contains(segment)) {

        int index = connectedNSegments.indexOf(segment);
       
        junctionCutCenters.add(coords.get(index));
        junctionCutVectors.add(cutVectors.get(index));
        junctionWidths.add(widths.get(index));
       
      } else {
       
        junctionCutCenters.add(null);
        junctionCutVectors.add(null);
        junctionWidths.add(null);
       
      }
     
    }
   
    nodeRepresentation.setInformation(
        junctionCutCenters, junctionCutVectors, junctionWidths);
   
  }

  /**
   * @param l  line with {@link NetworkWaySegmentWorldObject} as representation
   */
  private static void setOrthogonalCutVector(MapWaySegment l, boolean setStartVector) {

    VectorXZ cutVector = l.getRightNormal();
   
    NetworkWaySegmentWorldObject lRepresentation =
      (NetworkWaySegmentWorldObject)l.getPrimaryRepresentation();
   
    if (setStartVector) {
      lRepresentation.setStartCutVector(cutVector);
    } else {
      lRepresentation.setEndCutVector(cutVector);
    }
   
  }
 
}
TOP

Related Classes of org.osm2world.core.world.creation.NetworkCalculator

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.