Package sc

Source Code of sc.Racetrack2D

package sc;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Polygon;
import java.util.ArrayList;
import java.util.List;

import sc.math.CardinalCurve;
import sc.math.Spline2D;
import sc.math.Vector2D;

/**
* Racetrack2D provides methods to create and manage a C1-continuous closed
* spline. The intention, as the name suggests, is that this represents a
* racetrack.
*/
public final class Racetrack2D {

  // The tracks spline
  private final CardinalCurve splines_curve = new CardinalCurve();
 
  private final Spline2D spline = new Spline2D(this.splines_curve);

  // Half width of track
  private double halfwidth;

  // Number of intervals between successive control points
  private double interval;

  // Intervals of middle curve
  private List<double[]> midcurve;

    // Intervals of left curve
  private List<double[]> leftcurve;

    // Intervals of right curve
  private List<double[]> rightcurve;

  // List of polygons that make up the track
  private final List<Polygon> polygons = new ArrayList<Polygon>();

  /** Creates an instance of Racetrack2D */
  public Racetrack2D() {
    // Default attributes of Racetrack
    setInterval(10);
    setWidth(10);
    setTension(-0.5);
  }

  /**
   * Sets the spline interval of the race track.
   *
   * @param interval
   *       Number of intervals between successive control points
   */
  public void setInterval(double interval) {
    this.interval = interval;
  }

  /**
   * Gets the spline interval of the race track.
   *
   * @return Number of intervals between successive control points
   */
  public double getInterval() {
    return this.interval;
  }
 
  /**
   * Sets the width of the race track.
   *
   * @param width
   *       Width of track
   */
  public void setWidth(double width) {
    this.halfwidth = width * 0.5;
  }
 
  /**
   * Gets the width of the race track.
   *
   * @return Width of track
   */
  public double getWidth() {
    return this.halfwidth * 2;
  }
 
  /**
   * Sets the tension of the race tracks spline.
   *
   * @param tension
   *       New tension value
   */
  public void setTension(double tension) {
    this.splines_curve.setTension(tension);
  }
 
  /**
   * Gets the tension of the race tracks spline.
   *
   * @return Tension value
   */
  public double getTension() {
    return this.splines_curve.getTension();
  }
 
  /**
   * Gets the tracks Spline.
   *
   * @return the spline
   */
  public Spline2D getSpline() {
    return this.spline;
  }

  /**
   * Appends a new control point.
   *
   * @param x
   *       x-coordinate of new control point
   * @param y
   *       y-coordinate of new control point
   */
  public void appendControlPoint(double x, double y) {
    if (sanitise(x, y))
      this.spline.points.add(new double[] {x, y});
  }
 
  /** An example of how to render the track to a Graphics context */
  public void drawtrack(Graphics g) {
   
    g.setColor(new Color(0xff0000));

    for (Polygon p : polygons) {
      // g.drawLine((int) p.xpoints[0], (int) p.ypoints[0], (int)
      // p.xpoints[1],
      // (int) p.ypoints[1]);
      // g.drawLine((int) p.xpoints[2], (int) p.ypoints[2], (int)
      // p.xpoints[3],
      // (int) p.ypoints[3]);
      g.drawPolygon(p);
    }
   
    for (double[] p : getSpline().points) {
      g.fillOval((int)p[0], (int)p[1], 5, 5);
    }
  }
 
  /**
   * Scans angle and distance between the last three controls points ending with index.
   *
   * @param x
   *       x-coordinate of new control point
   * @param y
   *       y-coordinate of new control point
   */
  private boolean sanitise(double x, double y) {
    int last = this.spline.points.size() - 1;
    if (last < 1)
      return true;
   
    // Return false if last three control points form too sharp an angle.
    double[] p1 = this.spline.points.get(last - 1);
    double[] p2 = this.spline.points.get(last);
    double[] v1 = new double[2];
    double[] v2 = new double[2];
   
    // Calculate stuff
    Vector2D.vector(v1, p1[0], p1[1], p2[0], p2[1]);
    Vector2D.vector(v2, p2[0], p2[1], x, y);
   
   
    double length = Vector2D.magnitude(v2);
   
    // Control point has to be at least half track width away
    if (length < this.halfwidth)
      return false;
   
    // Calculate angle formed by last three control points
    double angle = Math.acos(Vector2D.dot(v1, v2) / (Vector2D.magnitude(v1)*Vector2D.magnitude(v2)))  * 180 / Math.PI;
   
    // Calculate max angle allowed
    double intervaldist = length / this.interval;
    double maxangle = ((Math.PI / 2) - Math.atan(intervaldist / this.halfwidth)) * 180 / Math.PI;
   
    return true;
  }
 
  /**
   * Updates the curves and polygons after a change has been made.
   */
  public void update() {
    // Don't do anything if there are not enough control points
    if (this.spline.points.size() < 4) return;
    // Else repeatedly call internal layout method until its happy with layout
    layout();
  }
 
  private int layout() {
    // Generate the curves intervals
    this.midcurve = this.spline.intervals(this.interval);
   
    Polygon polygon;
    double[][] left = new double[2][];
    double[][] right = new double[2][];
    this.leftcurve = new ArrayList<double[]>();
    this.rightcurve = new ArrayList<double[]>();
    double[][] p = new double[5][2];
      int last = this.midcurve.size() - 1;
   
    // Clear the current list of polygons
    this.polygons.clear();
   
    // Project the first left and right curve interval
    p[1] = this.midcurve.get(0);
    p[2] = this.midcurve.get(1);
    Vector2D.vector(p[3], p[1][0], p[1][1], p[2][0], p[2][1]);
    projectEdges(p, left, right);
    left[1] = left[0];
    right[1] = right[0];
   
    // Project the left and right curve intervals
    for (int i = 1; i < last; i++) {
      p[0] = this.midcurve.get(i - 1);
      p[1] = this.midcurve.get(i);
        p[2] = this.midcurve.get(i + 1);
        Vector2D.vector(p[3], p[0][0], p[0][1], p[1][0], p[1][1]);
          Vector2D.vector(p[4], p[1][0], p[1][1], p[2][0], p[2][1]);
          Vector2D.add(p[3], p[4]);
        projectEdges(p, left, right);
       
        //if (intervalsIntersect(left, right)) {
          // Work out which control point we're closest to
        //  int closest = (int)Math.round((i / this.interval)) + 1;
        //  straightenCorner(closest);
        //  return i;
        //}

      // Add the polygon
      polygon = new Polygon();
      polygon.addPoint((int) left[0][0], (int) left[0][1]);
      polygon.addPoint((int) left[1][0], (int) left[1][1]);
      polygon.addPoint((int) right[1][0], (int) right[1][1]);
      polygon.addPoint((int) right[0][0], (int) right[0][1]);
      this.polygons.add(polygon);

      // Remember current left/right points
      left[1] = left[0];
      right[1] = right[0];
    }
   
   
    // Project the last left and right curve interval
    p[0] = this.midcurve.get(last - 1);
    p[1] = this.midcurve.get(last);
    Vector2D.vector(p[3], p[0][0], p[0][1], p[1][0], p[1][1]);
    projectEdges(p, left, right);
   
    // Add the last polygon
    polygon = new Polygon();
    polygon.addPoint((int) left[0][0], (int) left[0][1]);
    polygon.addPoint((int) left[1][0], (int) left[1][1]);
    polygon.addPoint((int) right[1][0], (int) right[1][1]);
    polygon.addPoint((int) right[0][0], (int) right[0][1]);
    this.polygons.add(polygon);
   
    return -1;
  }
 
  /**
   * Used by update() to project the points on the left and right edges of the track.
   *
   * @param p
   *       Point array
   * @param left
   *       Left points array
   * @param right
   *       Right points array
   */
  private void projectEdges(double[][] p, double[][] left, double[][] right) {
    Vector2D.rotate90(p[3]);
    Vector2D.setLength(p[3], this.halfwidth);
    left[0] = new double[]{p[1][0]+p[3][0], p[1][1]+p[3][1]};
    this.leftcurve.add(left[0]);
    right[0] = new double[]{p[1][0]-p[3][0], p[1][1]-p[3][1]};
    this.rightcurve.add(right[0]);
  }
 
  /**
   * Used by update() to test for interval intersection.
   *
   * @param left
   *       Left points array
   * @param right
   *       Right points array
   * @return
   *      
   */
  private boolean intervalsIntersect(double[][] left, double[][] right) {
    double z1, z2;
    int s1, s2;
   
    if ((z1 = ((left[0][0]-left[1][0])*(right[1][1]-left[1][1])) - ((left[0][1]-left[1][1])*(right[1][0]-left[1][0]))) < 0)
      s1 = -1;
    else if (z1 > 0)
      s1 = 1;
    else
      s1 = 0;
   
    if ((z2 = ((right[0][0]-left[1][0])*(right[1][1]-left[1][1])) - ((right[0][1]-left[1][1])*(right[1][0]-left[1][0]))) < 0)
      s2 = -1;
    else if (z2 > 0)
      s2 = 1;
    else
      s2 = 0;
   
    if ((s1 == 0 || s2 == 0) || s1 != s2)
      return true;
   
    return false;
  }

  private void straightenCorner(int index) {
    double[][] p = new double[5][2];
    p[0] = this.spline.points.get(index - 1);
    p[1] = this.spline.points.get(index);
    p[2] = this.spline.points.get(index + 1);
    Vector2D.vector(p[3], p[1][0], p[1][1], p[0][0], p[0][1]);
      Vector2D.vector(p[4], p[1][0], p[1][1], p[2][0], p[2][1]);
      Vector2D.add(p[3], p[4]);
      Vector2D.setLength(p[3], this.halfwidth / 2);               
      p[1][0] += p[3][0];
      p[1][1] += p[3][1];
  }
}
TOP

Related Classes of sc.Racetrack2D

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.