Package com.vividsolutions.jtstest.testbuilder.topostretch

Source Code of com.vividsolutions.jtstest.testbuilder.topostretch.StretchedVertex

package com.vividsolutions.jtstest.testbuilder.topostretch;

import com.vividsolutions.jts.geom.*;
import com.vividsolutions.jts.math.MathUtil;
import com.vividsolutions.jts.math.Vector2D;
import com.vividsolutions.jts.algorithm.*;

/**
* Models a vertex of a Geometry which will be stretched
* due to being too near other segments and vertices.
* <p>
* Currently for simplicity a vertex is assumed to
* be near only one segment or other vertex.
* This is sufficient for most cases.
*
* @author Martin Davis
*
*/
public class StretchedVertex
{
  // TODO: also provide information about the segments around the facet the vertex is near to, to allow smarter adjustment
 
  private Coordinate vertexPt;
  private Coordinate[] parentLine;
  private int parentIndex;
  private Coordinate nearPt = null;
  private Coordinate[] nearPts = null;
  private int nearIndex = -1;
  private LineSegment nearSeg = null;
  private Coordinate stretchedPt = null;
 
  /**
   * Creates a vertex which lies near a vertex
   * @param vertexPt
   * @param parentLine
   * @param index
   * @param nearPt
   */
  public StretchedVertex(Coordinate vertexPt, Coordinate[] parentLine, int parentIndex,
      Coordinate nearPt, Coordinate[] nearPts, int nearIndex)
  {
    this.vertexPt = vertexPt;
    this.parentLine = parentLine;
    this.parentIndex = parentIndex;
    this.nearPt = nearPt;
    this.nearPts = nearPts;
    this.nearIndex = nearIndex;
  }
 
  /**
   * Creates a vertex for a point which lies near a line segment
   * @param vertexPt
   * @param parentLine
   * @param parentIndex
   * @param nearSeg
   */
  public StretchedVertex(Coordinate vertexPt, Coordinate[] parentLine, int parentIndex,
      LineSegment nearSeg)
  {
    this.vertexPt = vertexPt;
    this.parentLine = parentLine;
    this.parentIndex = parentIndex;
    this.nearSeg = nearSeg;
  }
 
  public Coordinate getVertexCoordinate()
  {
    return vertexPt;
  }
 
  /**
   * Gets the point which this near vertex will be stretched to
   * (by a given distance)
   *
   * @param dist the distance to adjust the point by
   * @return the stretched coordinate
   */
  public Coordinate getStretchedVertex(double dist)
  {
    if (stretchedPt != null)
      return stretchedPt;
   
    if (nearPt != null) {
      stretchedPt = displaceFromVertex(nearPt, dist);
//      stretchedPt = displaceFromPoint(nearPt, dist);
      // displace in direction of segment this pt lies on
    }
    else {
      stretchedPt = displaceFromSeg(nearSeg, dist);
    }
    return stretchedPt;
  }
 
  private boolean isNearRing()
  {
    return CoordinateArrays.isRing(nearPts);
  }
 
  private Coordinate getNearRingPoint(int i)
  {
    int index = i;
    if (i < 0)
      index = i + nearPts.length -1;
    else if (i >= nearPts.length - 1)
      index = i - (nearPts.length - 1);
    return nearPts[index];
  }
 
  private Coordinate displaceFromPoint(Coordinate nearPt, double dist)
  {
    LineSegment seg = new LineSegment(nearPt, vertexPt);
   
    // compute an adjustment which displaces in the direction of the nearPt-vertexPt vector
    // TODO: make this robust!
    double len = seg.getLength();
    double frac = (dist + len) / len;
    Coordinate strPt = seg.pointAlong(frac);
    return strPt;
  }
 
  private Coordinate displaceFromSeg(LineSegment nearSeg, double dist)
  {
    double frac = nearSeg.projectionFactor(vertexPt);
   
    // displace away from the segment on the same side as the original point
    int side = nearSeg.orientationIndex(vertexPt);
    if (side == CGAlgorithms.RIGHT)
      dist = -dist;
   
    return nearSeg.pointAlongOffset(frac, dist);
  }
 
  private Coordinate displaceFromVertex(Coordinate nearPt, double dist)
  {
    // handle linestring endpoints - do simple displacement
    if (! isNearRing()
        && nearIndex == 0 || nearIndex >= nearPts.length -1) {
      return displaceFromPoint(nearPt, dist);
    }
     
    // analyze corner to see how to displace the vertex
    // find corner points
    Coordinate p1 = getNearRingPoint(nearIndex - 1);
    Coordinate p2 = getNearRingPoint(nearIndex + 1);
   
    // if vertexPt is identical to an arm of the corner, just displace the point
    if (p1.equals2D(vertexPt) || p2.equals2D(vertexPt))
      return displaceFromPoint(nearPt, dist);
   
    return displaceFromCornerAwayFromArms(nearPt, p1, p2, dist);
  }
 
  private Coordinate displaceFromCornerOriginal(Coordinate nearPt, Coordinate p1, Coordinate p2, double dist)
  {
    // if corner is nearly flat, just displace point
    // TODO: displace from vertex on appropriate side of flat line, with suitable angle
    if (isFlat(nearPt, p1, p2))
      return displaceFromFlatCorner(nearPt, p1, p2, dist);

    Coordinate[] corner = orientCorner(nearPt, p1, p2);
   
    // find quadrant of corner that vertex pt lies in
    int quadrant = quadrant(vertexPt, nearPt, corner);
   
    Vector2D normOffset = normalizedOffset(nearPt, p1, p2);
    Vector2D baseOffset = normOffset.multiply(dist);
    Vector2D rotatedOffset = baseOffset.rotateByQuarterCircle(quadrant);
   
    return rotatedOffset.translate(vertexPt);
    //return null;
  }

  private Coordinate displaceFromCorner(Coordinate nearPt, Coordinate p1, Coordinate p2, double dist)
  {
    Coordinate[] corner = orientCorner(nearPt, p1, p2);
      // compute perpendicular bisector of p1-p2
    Vector2D u1 = Vector2D.create(nearPt, corner[0]).normalize();
    Vector2D u2 = Vector2D.create(nearPt, corner[1]).normalize();
    double ang = u1.angle(u2);
    Vector2D innerBisec = u2.rotate(ang / 2);
        Vector2D offset = innerBisec.multiply(dist);
    if (! isInsideCorner(vertexPt, nearPt, corner[0], corner[1])) {
        offset = offset.multiply(-1);
    }
    return offset.translate(vertexPt);
  }

  private static final double MAX_ARM_NEARNESS_ANG = 20.0 / 180.0 * Math.PI;
 
  private static double maxAngleToBisector(double ang)
  {
    double relAng = ang / 2 - MAX_ARM_NEARNESS_ANG;
    if (relAng < 0) return 0;
    return relAng;
  }
 
  /**
   * Displaces a vertex from a corner,
   * with angle limiting
   * used to ensure that the displacement is not close to the arms of the corner.
   *
   * @param nearPt
   * @param p1
   * @param p2
   * @param dist
   * @return
   */
  private Coordinate displaceFromCornerAwayFromArms(Coordinate nearPt, Coordinate p1, Coordinate p2, double dist)
  {
    Coordinate[] corner = orientCorner(nearPt, p1, p2);
    boolean isInsideCorner = isInsideCorner(vertexPt, nearPt, corner[0], corner[1]);
   
    Vector2D u1 = Vector2D.create(nearPt, corner[0]).normalize();
    Vector2D u2 = Vector2D.create(nearPt, corner[1]).normalize();
    double cornerAng = u1.angle(u2);
   
    double maxAngToBisec = maxAngleToBisector(cornerAng);
   
    Vector2D bisec = u2.rotate(cornerAng / 2);
    if (! isInsideCorner) {
      bisec = bisec.multiply(-1);
      double outerAng = 2 * Math.PI - cornerAng;
      maxAngToBisec = maxAngleToBisector(outerAng);
    }
   
    Vector2D pointwiseDisplacement = Vector2D.create(nearPt, vertexPt).normalize();
    double stretchAng = pointwiseDisplacement.angleTo(bisec);
    double stretchAngClamp = MathUtil.clamp(stretchAng, -maxAngToBisec, maxAngToBisec);
    Vector2D cornerDisplacement = bisec.rotate(-stretchAngClamp).multiply(dist);
    return cornerDisplacement.translate(vertexPt);
  }

  private boolean isInsideCorner(Coordinate queryPt, Coordinate base, Coordinate p1, Coordinate p2)
  {
      return CGAlgorithms.orientationIndex(base, p1, queryPt) == CGAlgorithms.CLOCKWISE
              && CGAlgorithms.orientationIndex(base, p2, queryPt) == CGAlgorithms.COUNTERCLOCKWISE;
  }

  private static final double POINT_LINE_FLATNESS_RATIO = 0.01;
 
  private static boolean isFlat(Coordinate p, Coordinate p1, Coordinate p2)
  {
    double dist = CGAlgorithms.distancePointLine(p, p1, p2);
    double len = p1.distance(p2);
    if (dist/len < POINT_LINE_FLATNESS_RATIO)
      return true;
    return false;
  }
 
  /**
   *
   * @param pt
   * @param cornerBase the two vertices defining the
   * @param corner the two vertices defining the arms of the corner, oriented CW
   * @return the quadrant the pt lies in
   */
  private static int quadrant(Coordinate pt, Coordinate cornerBase, Coordinate[] corner)
  {
    if (CGAlgorithms.orientationIndex(cornerBase, corner[0], pt) == CGAlgorithms.CLOCKWISE) {
      if (CGAlgorithms.orientationIndex(cornerBase, corner[1], pt) == CGAlgorithms.COUNTERCLOCKWISE) {
        return 0;
      }
      else
        return 3;
    }
    else {
      if (CGAlgorithms.orientationIndex(cornerBase, corner[1], pt) == CGAlgorithms.COUNTERCLOCKWISE) {
        return 1;
      }
      else
        return 2;    
    }
  }
 
  private static Coordinate rotateToQuadrant(Coordinate v, int quadrant)
  {
    switch (quadrant) {
    case 0: return v;
    case 1: return new Coordinate(-v.y, v.x);
    case 2: return new Coordinate(-v.x, -v.y);
    case 3: return new Coordinate(v.y, -v.x);
    }
    return null;
  }
 
  /**
   * Returns an array of pts such that p0 - p[0] - [p1] is CW.
   *
   * @param p0
   * @param p1
   * @param p2
   * @return
   */
  private static Coordinate[] orientCorner(Coordinate p0, Coordinate p1, Coordinate p2)
  {
    Coordinate[] orient;
    // TODO: not sure if determining orientation is necessary?
    if (CGAlgorithms.CLOCKWISE == CGAlgorithms.orientationIndex(p0, p1, p2)) {
      orient = new Coordinate[] {p1, p2 };
    }
    else {
      orient = new Coordinate[] { p2, p1 };
    }
   
    return orient;
  }
 
  /**
   * Returns an array of pts such that p0 - p[0] - [p1] is CW.
   *
   * @param p0
   * @param p1
   * @param p2
   * @return
   */
  private static Vector2D normalizedOffset(Coordinate p0, Coordinate p1, Coordinate p2)
  {
    Vector2D u1 = Vector2D.create(p0, p1).normalize();
    Vector2D u2 = Vector2D.create(p0, p2).normalize();
    Vector2D offset = u1.add(u2).normalize();
    return offset;
  }
 
  private Coordinate displaceFromFlatCorner(Coordinate nearPt, Coordinate p1, Coordinate p2, double dist)
  {
    // compute perpendicular bisector of p1-p2
    Vector2D bisecVec = Vector2D.create(p2, p1).rotateByQuarterCircle(1);
    Vector2D offset = bisecVec.normalize().multiply(dist);
    return offset.translate(vertexPt);
  }
}
TOP

Related Classes of com.vividsolutions.jtstest.testbuilder.topostretch.StretchedVertex

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.