package sim.physics2D.shape;
import java.awt.Graphics2D;
import sim.portrayal.DrawInfo2D;
//import sim.physics2D.util.Double2D;
import sim.util.matrix.*;
import sim.util.Double2D;
/** Polygons represents any convex multi-sided object. Convex means
* that every angle measured between the insides of two edges must be
* less than 180 degrees. To create a new polygon, create a new class
* that inherits from polygon and implement the abstract functions based
* on the descriptions given below. Use rectangle as a reference.
*/
public abstract class Polygon extends Shape
{
protected double maxXDistanceFromCenter;
protected double maxYDistanceFromCenter;
protected DenseMatrix vertices;
protected DenseMatrix edges;
protected DenseMatrix normals;
private Double2D[] verticesCache;
private Double2D[] edgesCache;
private Double2D[] normalsCache;
private boolean vertCacheValid;
private boolean edgesCacheValid;
private boolean normalsCacheValid;
public Polygon(boolean stationary)
{
super(stationary);
vertCacheValid = false;
edgesCacheValid = false;
normalsCacheValid = false;
}
public Polygon()
{
this(false);
}
protected DenseMatrix scale = new DenseMatrix(new double[][]
{{0, 0, 0},
{0, 0, 0},
{0, 0, 1}});
/** Returns a list of the vertexes in a clockwise direction with
* positive Y pointing up (vs. pointing down as on a computer screen) */
public Double2D[] getVertices()
{
if (vertCacheValid)
return verticesCache;
else
{
DenseMatrix rotTranDenseMatrix = Polygon.rotationTranslationMatrix2D(this.getOrientation().radians, this.getPosition());
DenseMatrix rotVertices = rotTranDenseMatrix.times(this.vertices);
Double2D[] verts = new Double2D[rotVertices.n];
for (int i = 0; i < rotVertices.n; i++)
verts[i] = new Double2D(rotVertices.vals[0][i], rotVertices.vals[1][i]);
verticesCache = verts;
if (stationary)
vertCacheValid = true;
return verticesCache;
}
}
/** Returns a list of the normalized edges in a clockwise direction. The
* starting vertex of the edge must be the vertex with the corresponding index.
* For example, edge 0 goes from vertex 0 to vertex 1 */
public Double2D[] getEdges()
{
if (edgesCacheValid)
return edgesCache;
else
{
DenseMatrix rotDenseMatrix = Polygon.rotationTranslationMatrix2D(this.getOrientation().radians, new Double2D(0,0));
DenseMatrix rotEdges = rotDenseMatrix.times(this.edges);
Double2D[] result = new Double2D[rotEdges.n];
for (int i = 0; i < rotEdges.n; i++)
result[i] = new Double2D(rotEdges.vals[0][i], rotEdges.vals[1][i]);
edgesCache = result;
if (stationary) edgesCacheValid = true;
return edgesCache;
}
}
/** Returns a list of the unit normals in a clockwise direction. Each
* normal's index must correspond to its edge's index. For example,
* the normal to edge 0 must have index 0 */
public Double2D[] getNormals()
{
if (normalsCacheValid)
return normalsCache;
else
{
DenseMatrix rotDenseMatrix = Polygon.rotationTranslationMatrix2D(this.getOrientation().radians, new Double2D(0,0));
DenseMatrix rotNormals = rotDenseMatrix.times(this.normals);
Double2D[] result = new Double2D[rotNormals.n];
for (int i = 0; i < rotNormals.n; i++)
result[i] = new Double2D(rotNormals.vals[0][i], rotNormals.vals[1][i]);
if (stationary)
{
normalsCache = result;
normalsCacheValid = true;
}
return result;
}
}
// force polygons to set stuff up correctly
/** Set up the vertices DenseMatrix. The vertices DenseMatrix gives the
* homogenous coordinates of the vertices centered around 0,0.
* They must be defined clockwise. */
abstract public void initVertices();
/** Set up the edges DenseMatrix. The edges DenseMatrix gives the edge
* vectors in homogenous coordinates between the
* vertices. The number of the edge should
* correspond to the number of the vertex from which the vector
* points (i.e. edge 0 points from vertex 0 to vertex 1). Edges
* should point clockwise and must be normalized. */
abstract public void initEdges();
/** Set up the normals DenseMatrix. The normals DenseMatrix gives
* the normal vectors in homogenous coordinates. The numbers of
* the normals must correspond to the edge to which the normal
* is perpendicular to (i.e. normal 0 points out from edge 0).
* The normals must be of unit length */
abstract public void initNormals();
/** Display the polygon */
public void draw(Object object, Graphics2D graphics, DrawInfo2D info)
{
graphics.setPaint(paint);
// set the scaling DenseMatrix
scale.vals[0][0] = info.draw.width;
scale.vals[1][1] = info.draw.height;
DenseMatrix scaledMat = scale.times(this.vertices);
DenseMatrix rottedMat = Polygon.rotationTranslationMatrix2D(getOrientation().radians, new Double2D(info.draw.x, info.draw.y)).times(scaledMat);
graphics.fillPolygon(Polygon.getRow(0, rottedMat),Polygon.getRow(1, rottedMat), this.vertices.n);
}
public double getMaxXDistanceFromCenter()
{
return this.maxXDistanceFromCenter;
}
public double getMaxYDistanceFromCenter()
{
return this.maxYDistanceFromCenter;
}
/** Returns a DenseMatrix in homogenous coordinates to rotate a 2 dimensional
* rigid body given the angle theta (in radians)
*/
public static DenseMatrix rotationMatrix2D(double theta)
{
double cosTheta = Math.cos(theta);
double sinTheta = Math.sin(theta);
double[][] vals =
{{cosTheta, -sinTheta},
{sinTheta, cosTheta},
{0, 1}};
return new DenseMatrix(vals);
}
/** Returns a DenseMatrix in homogenous coordinates to rotate and translate
* a 2 dimensional rigid body given the angle theta (in radians) and a translation vector
*/
public static DenseMatrix rotationTranslationMatrix2D(double theta, Double2D translation)
{
double cosTheta = Math.cos(theta);
double sinTheta = Math.sin(theta);
double[][] vals =
{{cosTheta, -sinTheta, translation.x},
{sinTheta, cosTheta, translation.y},
{0, 0, 1}};
return new DenseMatrix(vals);
}
/** Returns a row of the DenseMatrix rounded to integers */
public static int[] getRow(int row, DenseMatrix mat)
{
int cols = mat.n;
int[] result = new int[cols];
for (int i = 0; i < cols; i++)
result[i] = (int)Math.round(mat.vals[row][i]);
return result;
}
}