All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.metacsp.spatial.geometry.Polygon Maven / Gradle / Ivy

There is a newer version: 1.3.5
Show newest version
package org.metacsp.spatial.geometry;

import java.util.Vector;

import org.metacsp.framework.ConstraintSolver;
import org.metacsp.framework.Domain;
import org.metacsp.framework.Variable;

public class Polygon extends Variable {

	private static final long serialVersionUID = 3385252449426376306L;
		
	private Domain dom;
	
	public static final int MAX_POLY_VERTEX_COUNT = 260;
	private int vertexCount;
	private boolean isMovable = true;
	
//	private Vec2[] vertices = Vec2.arrayOf( MAX_POLY_VERTEX_COUNT );
	public Vec2[] normals = Vec2.arrayOf( MAX_POLY_VERTEX_COUNT );
	public Mat2 u = new Mat2();
	
	private Vec2 position = new Vec2();
	public boolean ref = false;
	public boolean incident = false;
	
	private boolean hasDefaultDomain = true;
	
	private float orientation;
	
	protected Polygon(ConstraintSolver cs, int id) {
		super(cs, id);
		this.setInitialDomain();
	}

	/**
	 * Returns whether or not the domain of this {@link Polygon} has been refined.
	 * @return true iff the domain of this {@link Polygon} has been refined.
	 */
	public boolean hasDefaultDomain() {
		return hasDefaultDomain;
	}
	
	/**
	 * Set whether this {@link Polygon} is movable. 
	 * @param isMovable Whether this {@link Polygon} is movable.
	 */
	public void setMovable(boolean isMovable){
		this.isMovable = isMovable;
	}

	/**
	 * Query whether this {@link Polygon} is movable.
	 * @return true iff this {@link Polygon} is movable.
	 */
	public boolean isMovable(){
		return this.isMovable;
	}

	/**
	 * Get number of vertices in this {@link Polygon}.
	 * @return The number of vertices in this {@link Polygon}.
	 */
	public int getVertexCount(){
		return vertexCount;
	}
	
	@Override
	public int compareTo(Variable arg0) {
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public Domain getDomain() {
		// TODO Auto-generated method stub
		return dom;
	}

	@Override
	public void setDomain(Domain d) {
		
	}
	
	/**
	 * Set the domain of this {@link Polygon}, its vertices.
	 * @param verts The vertices of this {@link Polygon}.
	 */
	public void setDomain(Vec2 ... verts){
		dom = new Vertex(this);
		vertexCount = verts.length;
		this.setOrient(0.0f);
		getPosition().set( getCentroid(orderVertex(verts)).x, getCentroid(orderVertex(verts)).y );
		Vec2[] halfPoly = new Vec2[verts.length];
		for (int i = 0; i < verts.length; i++) {
			halfPoly[i] = new Vec2(verts[i].x - getPosition().x, verts[i].y - getPosition().y);
		}
		set( halfPoly );
		initialize();
		hasDefaultDomain = false;
	}
	
	private void setInitialDomain() {
		Vec2 p1 = new Vec2(GeometricConstraintSolver.MIN_X,GeometricConstraintSolver.MAX_Y);
		Vec2 p2 = new Vec2(GeometricConstraintSolver.MAX_X,GeometricConstraintSolver.MAX_Y);
		Vec2 p3 = new Vec2(GeometricConstraintSolver.MAX_X,GeometricConstraintSolver.MIN_Y);
		Vec2 p4 = new Vec2(GeometricConstraintSolver.MIN_X,GeometricConstraintSolver.MIN_Y);
		this.setDomain(p1,p2,p3,p4);
		hasDefaultDomain = true;
	}
	
	@Override
	public String toString() {
		String ret = this.getClass().getSimpleName() + " " + this.id;
		Vertex dom = (Vertex)this.getDomain();
		for (int i = 0; i < vertexCount; i++) ret += (" " + dom.getVertices()[i]);
		return ret;
	}

	private void initialize()
	{
		computeMass( 1.0f );
	}
	
	private void computeMass( float density )
	{
		// Calculate centroid and moment of inertia
		
		float area = 0.0f;
		float I = 0.0f;
		final float k_inv3 = 1.0f / 3.0f;
		Vec2 c = new Vec2( 0.0f, 0.0f ); // centroid;

		for (int i = 0; i < vertexCount; ++i)
		{
			// Triangle vertices, third vertex implied as (0, 0)
			Vec2 p1 = ((Vertex)this.dom).getVertices()[i];
			Vec2 p2 = ((Vertex)this.dom).getVertices()[(i + 1) % vertexCount];

			float D = Vec2.cross( p1, p2 );
			float triangleArea = 0.5f * D;

			area += triangleArea;

			// Use area to weight the centroid average, not just vertex position
			float weight = triangleArea * k_inv3;
			c.addsi( p1, weight );
			c.addsi( p2, weight );

			float intx2 = p1.x * p1.x + p2.x * p1.x + p2.x * p2.x;
			float inty2 = p1.y * p1.y + p2.y * p1.y + p2.y * p2.y;
			I += (0.25f * k_inv3 * D) * (intx2 + inty2);
		}

		c.muli( 1.0f / area );

		// Translate vertices to centroid (make the centroid (0, 0)
		// for the polygon in model space)
		// Not really necessary, but I like doing this anyway
		for (int i = 0; i < vertexCount; ++i)
		{
			((Vertex)this.dom).getVertices()[i].subi( c );
		}
		
//		this.mass = density * area;
//		this.invMass = (this.mass != 0.0f) ? 1.0f / this.mass : 0.0f;
//		this.inertia = I * density;
//		this.invInertia = (this.inertia != 0.0f) ? 1.0f / this.inertia : 0.0f;
	}

	private Vec2 getCentroid(Vec2 ...vers)
	{
		// Calculate centroid and moment of inertia
		
		Vec2[] verticesCopy = new Vec2[vers.length];
		for (int i = 0; i < vers.length; i++) {
			verticesCopy[i] = new Vec2(vers[i].x, vers[i].y);
		}
		
		float area = 0.0f;
		float I = 0.0f;
		final float k_inv3 = 1.0f / 3.0f;
		Vec2 c = new Vec2( 0.0f, 0.0f ); // centroid;

		for (int i = 0; i < vertexCount; ++i)
		{
			// Triangle vertices, third vertex implied as (0, 0)
			Vec2 p1 = verticesCopy[i];
			Vec2 p2 = verticesCopy[(i + 1) % vertexCount];

			float D = Vec2.cross( p1, p2 );
			float triangleArea = 0.5f * D;

			area += triangleArea;

			// Use area to weight the centroid average, not just vertex position
			float weight = triangleArea * k_inv3;
			c.addsi( p1, weight );
			c.addsi( p2, weight );

			float intx2 = p1.x * p1.x + p2.x * p1.x + p2.x * p2.x;
			float inty2 = p1.y * p1.y + p2.y * p1.y + p2.y * p2.y;
			I += (0.25f * k_inv3 * D) * (intx2 + inty2);
		}

		c.muli( 1.0f / area );

		// Translate vertices to centroid (make the centroid (0, 0)
		// for the polygon in model space)
		// Not really necessary, but I like doing this anyway
		for (int i = 0; i < vertexCount; ++i)
		{
			verticesCopy[i].subi( c );
		}		
		return c;
	
	}

	/**
	 * Get the orientation of this polygon (in radians).
	 * @return The orientation of this polygon (in radians).
	 */
	public float getOrientation() {
		return orientation;
	}
	
	/**
	 * Set the orientation of this polygon (in radians).
	 * @param or The orientation of this polygon (in radians).
	 */
	public void setOrientation(float or) {
		this.orientation = or;
	}
	
	private void setOrient( float radians )
	{
		u.set( radians );
	}

	private void setBox( float hw, float hh )
	{
		Vec2[] vertices = Vec2.arrayOf( MAX_POLY_VERTEX_COUNT );
		vertexCount = 4;
		vertices[0].set( -hw, -hh );
		vertices[1].set( hw, -hh );
		vertices[2].set( hw, hh );
		vertices[3].set( -hw, hh );
		
		((Vertex)this.dom).setVertices(vertices);
		
		normals[0].set( 0.0f, -1.0f );
		normals[1].set( 1.0f, 0.0f );
		normals[2].set( 0.0f, 1.0f );
		normals[3].set( -1.0f, 0.0f );
	}
	
	private Vec2[] orderVertex(Vec2 ...verts){
		
		Vec2[] vertices = Vec2.arrayOf( MAX_POLY_VERTEX_COUNT );
		Vec2[] verticesCopy = new Vec2[verts.length];
		for (int i = 0; i < verts.length; i++) {
			verticesCopy[i] = new Vec2(verts[i].x, verts[i].y);
		}
		
		
		
		int rightMost = 0;
		float highestXCoord = verticesCopy[0].x;
		for (int i = 1; i < verticesCopy.length; ++i)
		{
			float x = verticesCopy[i].x;

			if (x > highestXCoord)
			{
				highestXCoord = x;
				rightMost = i;
			}
			// If matching x then take farthest negative y
			else if (x == highestXCoord)
			{
				if (verticesCopy[i].y < verticesCopy[rightMost].y)
				{
					rightMost = i;
				}
			}
		}

		int[] hull = new int[MAX_POLY_VERTEX_COUNT];
		int outCount = 0;
		int indexHull = rightMost;

		for (;;)
		{
			hull[outCount] = indexHull;

			// Search for next index that wraps around the hull
			// by computing cross products to find the most counter-clockwise
			// vertex in the set, given the previos hull index
			int nextHullIndex = 0;
			for (int i = 1; i < verticesCopy.length; ++i)
			{
				// Skip if same coordinate as we need three unique
				// points in the set to perform a cross product
				if (nextHullIndex == indexHull)
				{
					nextHullIndex = i;
					continue;
				}

				// Cross every set of three unique vertices
				// Record each counter clockwise third vertex and add
				// to the output hull
				// See : http://www.oocities.org/pcgpe/math2d.html
				Vec2 e1 = verticesCopy[nextHullIndex].sub( verticesCopy[hull[outCount]] );
				Vec2 e2 = verticesCopy[i].sub( verticesCopy[hull[outCount]] );
				float c = Vec2.cross( e1, e2 );
				if (c < 0.0f)
				{
					nextHullIndex = i;
				}

				// Cross product is zero then e vectors are on same line
				// therefore want to record vertex farthest along that line
				if (c == 0.0f && e2.lengthSq() > e1.lengthSq())
				{
					nextHullIndex = i;
				}
			}

			++outCount;
			indexHull = nextHullIndex;

			// Conclude algorithm upon wrap-around
			if (nextHullIndex == rightMost)
			{
				vertexCount = outCount;
				break;
			}
		}

		// Copy vertices into shape's vertices
		for (int i = 0; i < vertexCount; ++i)
		{
			vertices[i].set( verticesCopy[hull[i]] );
		}
		return vertices;
	}
	
	private void set( Vec2... verts )
	{
		// Find the right most point on the hull
		Vec2[] vertices = Vec2.arrayOf( MAX_POLY_VERTEX_COUNT );
		int rightMost = 0;
		float highestXCoord = verts[0].x;
		for (int i = 1; i < verts.length; ++i)
		{
			float x = verts[i].x;

			if (x > highestXCoord)
			{
				highestXCoord = x;
				rightMost = i;
			}
			// If matching x then take farthest negative y
			else if (x == highestXCoord)
			{
				if (verts[i].y < verts[rightMost].y)
				{
					rightMost = i;
				}
			}
		}

		int[] hull = new int[MAX_POLY_VERTEX_COUNT];
		int outCount = 0;
		int indexHull = rightMost;

		for (;;)
		{
			hull[outCount] = indexHull;

			// Search for next index that wraps around the hull
			// by computing cross products to find the most counter-clockwise
			// vertex in the set, given the previos hull index
			int nextHullIndex = 0;
			for (int i = 1; i < verts.length; ++i)
			{
				// Skip if same coordinate as we need three unique
				// points in the set to perform a cross product
				if (nextHullIndex == indexHull)
				{
					nextHullIndex = i;
					continue;
				}

				// Cross every set of three unique vertices
				// Record each counter clockwise third vertex and add
				// to the output hull
				// See : http://www.oocities.org/pcgpe/math2d.html
				Vec2 e1 = verts[nextHullIndex].sub( verts[hull[outCount]] );
				Vec2 e2 = verts[i].sub( verts[hull[outCount]] );
				float c = Vec2.cross( e1, e2 );
				if (c < 0.0f)
				{
					nextHullIndex = i;
				}

				// Cross product is zero then e vectors are on same line
				// therefore want to record vertex farthest along that line
				if (c == 0.0f && e2.lengthSq() > e1.lengthSq())
				{
					nextHullIndex = i;
				}
			}

			++outCount;
			indexHull = nextHullIndex;

			// Conclude algorithm upon wrap-around
			if (nextHullIndex == rightMost)
			{
				vertexCount = outCount;
				break;
			}
		}

		// Copy vertices into shape's vertices
		for (int i = 0; i < vertexCount; ++i)
		{
			vertices[i].set( verts[hull[i]] );
		}

		// Compute face normals
		for (int i = 0; i < vertexCount; ++i)
		{
			Vec2 face = vertices[(i + 1) % vertexCount].sub( vertices[i] );

			// Calculate normal with 2D cross product between vector and scalar
			normals[i].set( face.y, -face.x );
			normals[i].normalize();
		}
		((Vertex)this.dom).setVertices(vertices);
	}

	
	public Vec2 getSupport( Vec2 dir )
	{
		float bestProjection = -Float.MAX_VALUE;
		Vec2 bestVertex = null;

		for (int i = 0; i < vertexCount; ++i)
		{
			Vec2 v = ((Vertex)this.dom).getVertices()[i];
			float projection = Vec2.dot( v, dir );

			if (projection > bestProjection)
			{
				bestVertex = v;
				bestProjection = projection;
			}
		}
		return bestVertex;
	}

	/**
	 * Get the reference position of this {@link Polygon}.
	 * @return The reference position of this {@link Polygon}.
	 */
	public Vec2 getPosition() {
		return position;
	}

	/**
	 * Set the reference position of this {@link Polygon}.
	 * @param position The reference position of this {@link Polygon}.
	 */
	public void setPosition(Vec2 position) {
		this.position = position;
	}
	
//	public Vector getShiftedPolygon(){
//		
//		Vector vecs = new Vector();
////		System.out.println(position.x);
////		System.out.println(position.y);
//		System.out.println("-----------------------------------------");		
//		for (int i = 0; i < vertexCount; i++){
//			Vec2 v = new Vec2( ((Vertex)this.dom).getVertices()[i] );
//			this.u.muli( v );
//			v.addi( position );
//			((Vertex)this.dom).getVertices()[i] = v;
//			vecs.add(v);
//			System.out.println("--> " +v.x + " " + v.y);
//		}
//		System.out.println("-----------------------------------------");
//		return vecs;
//	}
	
	public Vector getFullSpaceRepresentation(){
		
		Vector vecs = new Vector();
//		System.out.println(position.x);
//		System.out.println(position.y);
//		System.out.println("-----------------------------------------");		
		for (int i = 0; i < vertexCount; i++){
			Vec2 v = new Vec2( ((Vertex)this.dom).getVertices()[i] );
			this.u.muli( v );
			v.addi( position );
//			((Vertex)this.dom).getVertices()[i] = v;
			vecs.add(v);
//			System.out.println("--> " +v.x + " " + v.y);
		}
//		System.out.println("-----------------------------------------");
		return vecs;
	}

	
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy