/***********************************************************************
* mt4j Copyright (c) 2008 - 2009, C.Ruff, Fraunhofer-Gesellschaft All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
***********************************************************************/
package org.mt4j.components.bounds;
import org.mt4j.components.MTComponent;
import org.mt4j.components.TransformSpace;
import org.mt4j.components.visibleComponents.shapes.AbstractShape;
import org.mt4j.util.camera.IFrustum;
import org.mt4j.util.math.Matrix;
import org.mt4j.util.math.Ray;
import org.mt4j.util.math.ToolsGeometry;
import org.mt4j.util.math.Vector3D;
import processing.core.PGraphics;
/**
* Bounding rectangle for 2D shapes that are parallel to the Z=0 plane.
*
* @author Christopher Ruff
*/
public class BoundsZPlaneRectangle implements IBoundingShape {
// /** The peer component. */
private MTComponent peerComponent;
/** The bounding points local. */
private Vector3D[] boundingPointsLocal;
/** The I n_ plan e_ tolerance. */
public static float IN_PLANE_TOLERANCE = 0.015f;
private Vector3D centerPointLocal;
private Vector3D[] worldVecs;
private boolean worldVecsDirty;
private Vector3D centerPointWorld;
private boolean centerWorldDirty;
//TODO checken ob punkte wirklich in ebene liegen
//-> point in plane test
/**
* Instantiates a new bounds z plane rectangle.
*
* @param peerComponent the peer component
*/
public BoundsZPlaneRectangle(AbstractShape peerComponent) {
this(peerComponent, peerComponent.getVerticesLocal());
}
/**
* Instantiates a new bounds z plane rectangle.
*
* @param peerComponent the peer component
* @param x the x
* @param y the y
* @param width the width
* @param height the height
*/
public BoundsZPlaneRectangle(MTComponent peerComponent, float x, float y, float width, float height) {
this(peerComponent, new Vector3D[]{new Vector3D(x,y), new Vector3D(x+width,y), new Vector3D(x+width,y+height), new Vector3D(x,y+height)});
}
/**
* Instantiates a new bounds z plane rectangle.
*
* @param peerComponent the peer component
* @param vertices the vertices
*/
public BoundsZPlaneRectangle(MTComponent peerComponent, Vector3D[] vertices) {
super();
this.peerComponent = peerComponent;
this.boundingPointsLocal = this.getBoundingRectVertices(vertices);
this.centerPointLocal = this.calcCenterPointLocal();
this.worldVecsDirty = true;
this.centerWorldDirty = true;
// this.worldVecs = this.getVectorsGlobal();
// this.centerPointWorld = this.getCenterPointGlobal();
}
public void setGlobalBoundsChanged(){
this.worldVecsDirty = true;
this.centerWorldDirty = true;
}
/**
* Calculates the bounding rectangles vertices and returns them.
*
* @param vertices the vertices
*
* @return a Vector3D array containing the vertices of the bounding rectangle
*/
private Vector3D[] getBoundingRectVertices(Vector3D[] vertices){
float[] minMax = ToolsGeometry.getMinXYMaxXY(vertices);
float minX = minMax[0];
float minY = minMax[1];
float maxX = minMax[2];
float maxY = minMax[3];
float z = vertices[0].z;
if (peerComponent != null && peerComponent instanceof AbstractShape){
z = ((AbstractShape)peerComponent).getCenterPointLocal().z;
}
return (new Vector3D[]{
new Vector3D(minX, minY,z),
new Vector3D(maxX, minY,z),
new Vector3D(maxX, maxY,z),
new Vector3D(minX, maxY,z),
// new Vector3D(minX, minY,z)
});
}
public void drawBounds(final PGraphics g){
g.pushMatrix();
g.pushStyle();
g.fill(150,180);
g.beginShape();
g.vertex(this.boundingPointsLocal[0].x, this.boundingPointsLocal[0].y, this.boundingPointsLocal[0].z);
g.vertex(this.boundingPointsLocal[1].x, this.boundingPointsLocal[1].y, this.boundingPointsLocal[1].z);
g.vertex(this.boundingPointsLocal[2].x, this.boundingPointsLocal[2].y, this.boundingPointsLocal[2].z);
g.vertex(this.boundingPointsLocal[3].x, this.boundingPointsLocal[3].y, this.boundingPointsLocal[3].z);
g.endShape();
g.popStyle();
g.popMatrix();
// g.pushMatrix();
// g.pushStyle();
// g.fill(150,180);
//
// Vector3D[] v = this.getVectorsGlobal();
// float[] minMax = Tools3D.getMinXYMaxXY(v);
//
// g.beginShape();
// g.vertex(minMax[0], minMax[1], 0);
// g.vertex(minMax[2], minMax[1], 0);
// g.vertex(minMax[2], minMax[3], 0);
// g.vertex(minMax[0], minMax[3], 0);
// g.endShape();
////
// g.popStyle();
// g.popMatrix();
}
public boolean containsPointLocal(Vector3D testPoint) {
if ( testPoint.x >= this.boundingPointsLocal[0].x
&& testPoint.x <= this.boundingPointsLocal[1].x
&& testPoint.y >= this.boundingPointsLocal[0].y
&& testPoint.y <= this.boundingPointsLocal[2].y
&& Math.abs(testPoint.z - this.boundingPointsLocal[0].z) < IN_PLANE_TOLERANCE //Check if point roughly in same z plane as the rect with tolerance
){
return true;
}else{
return false;
}
}
public boolean intersects(BoundsZPlaneRectangle boundingRect){
//TODO actually we would have to check all rectangle
//line segments against each other instead of the points
Vector3D[] globalBoundingVectorsR2 = boundingRect.getVectorsGlobal();
Vector3D globalCenterR2 = boundingRect.getCenterPointGlobal();
boolean colliding = false;
//Check if rectangle points lie inside of this rectangle
for (int i = 0; i < globalBoundingVectorsR2.length; i++) {
Vector3D localVectorR2 = peerComponent.globalToLocal(globalBoundingVectorsR2[i]);
if (this.containsPointLocal(localVectorR2)){
colliding = true;
}
}
//Check rectangle center
if (this.containsPointLocal(peerComponent.globalToLocal(globalCenterR2))){
colliding = true;
}
//System.out.println("Colliding: " + colliding);
return colliding;
}
/** The rect normal. */
private Vector3D rectNormal = new Vector3D(0,0,1);
public Vector3D getIntersectionLocal(Ray ray) {
Vector3D[] verts = this.boundingPointsLocal;
// rectNormal= this.getNormalObjSpace();
//Normal should actually always be (0,0,1)!
Vector3D testPoint = ToolsGeometry.getRayPlaneIntersection(ray, rectNormal, verts[0]);
if (testPoint == null){
return null;
}
return (this.containsPointLocal(testPoint) ? testPoint : null);
}
/**
* Gets the normal obj space.
*
* @return the normal obj space
*/
private Vector3D getNormalLocal() {
return ToolsGeometry.getNormal(this.boundingPointsLocal[0], this.boundingPointsLocal[1], this.boundingPointsLocal[2], true);
}
private Vector3D calcCenterPointLocal(){
Vector3D tmp0 = this.boundingPointsLocal[0].getCopy();
Vector3D tmp1 = this.boundingPointsLocal[1].getSubtracted(this.boundingPointsLocal[0]);
tmp1.scaleLocal(0.5f);
Vector3D tmp2 = this.boundingPointsLocal[3].getSubtracted(this.boundingPointsLocal[0]);
tmp2.scaleLocal(0.5f);
tmp0.addLocal(tmp1);
tmp0.addLocal(tmp2);
return tmp0;
}
public Vector3D getCenterPointLocal() {
// Vector3D tmp0 = this.boundingPointsLocal[0].getCopy();
// Vector3D tmp1 = this.boundingPointsLocal[1].getSubtracted(this.boundingPointsLocal[0]);
// tmp1.scaleLocal(0.5f);
//
// Vector3D tmp2 = this.boundingPointsLocal[3].getSubtracted(this.boundingPointsLocal[0]);
// tmp2.scaleLocal(0.5f);
//
// tmp0.addLocal(tmp1);
// tmp0.addLocal(tmp2);
// return tmp0;
return this.centerPointLocal.getCopy();
}
public Vector3D getCenterPointGlobal() {
if (centerWorldDirty){
Vector3D tmp = this.getCenterPointLocal();
tmp.transform(this.peerComponent.getGlobalMatrix());
// tmp = peerComponent.localToGlobal(tmp);
this.centerPointWorld = tmp;
this.centerWorldDirty = false;
return this.centerPointWorld;
}
else{
return this.centerPointWorld;
}
}
public Vector3D[] getVectorsLocal() {
return this.boundingPointsLocal;
}
//FIXME also cache?
public Vector3D[] getVectorsRelativeToParent(){
Vector3D[] vecs = Vector3D.getDeepVertexArrayCopy(this.boundingPointsLocal);
Vector3D.transFormArrayLocal(this.peerComponent.getLocalMatrix(), vecs);
return vecs;
}
public Vector3D[] getVectorsGlobal() {
if (this.worldVecsDirty){
Vector3D[] vecs = Vector3D.getDeepVertexArrayCopy(this.boundingPointsLocal);
Vector3D.transFormArrayLocal(this.peerComponent.getGlobalMatrix(), vecs);
this.worldVecs = vecs;
this.worldVecsDirty = false;
return this.worldVecs;
}else{
return this.worldVecs;
}
// Vector3D[] vecs = Vector3D.getDeepVertexArrayCopy(this.boundingPointsLocal);
// Vector3D.transFormArrayLocal(this.peerComponent.getAbsoluteLocalToWorldMatrix(), vecs);
// return vecs;
}
public float getHeightXY(TransformSpace transformSpace) {
switch (transformSpace) {
case LOCAL:
return this.getHeightXYLocal();
case RELATIVE_TO_PARENT:
return this.getHeightXYRelativeToParent();
case GLOBAL:
return this.getHeightXYGlobal();
default:
return -1;
}
}
/**
* Gets the height xy obj space.
*
* @return the height xy obj space
*/
private float getHeightXYLocal() {
return this.getHeightXYVectLocal().length();
}
/**
* Gets the height xy relative to parent.
*
* @return the height xy relative to parent
*/
private float getHeightXYRelativeToParent() {
Vector3D p = this.getHeightXYVectLocal();
Matrix m = new Matrix(this.peerComponent.getLocalMatrix());
m.removeTranslationFromMatrix();
p.transform(m);
return p.length();
// Vector3D[] v = this.getVectorsRelativeToParent();
// float[] minMax = ToolsGeometry.getMinXYMaxXY(v);
// return minMax[3] - minMax[1];
}
/**
* Gets the height xy global.
*
* @return the height xy global
*/
private float getHeightXYGlobal() {
Vector3D p = this.getHeightXYVectLocal();
Matrix m = new Matrix(this.peerComponent.getGlobalMatrix());
m.removeTranslationFromMatrix();
p.transform(m);
return p.length();
// Vector3D[] v = this.getVectorsGlobal();
// float[] minMax = ToolsGeometry.getMinXYMaxXY(v);
// return minMax[3] - minMax[1];
}
/**
* Gets the "height vector". The vector is calculated from the bounds vectors,
* representing a vector with the height as its length in object space.
*
* @return the height xy vect obj space
*
* vector representing the height of the boundingshape of the shape
*/
public Vector3D getHeightXYVectLocal() {
Vector3D[] boundRectVertsLocal = this.getVectorsLocal();
Vector3D height = boundRectVertsLocal[2].getSubtracted(boundRectVertsLocal[1]);
return height;
}
public float getWidthXY(TransformSpace transformSpace) {
switch (transformSpace) {
case LOCAL:
return this.getWidthXYLocal();
case RELATIVE_TO_PARENT:
return this.getWidthXYRealtiveToParent();
case GLOBAL:
return this.getWidthXYGlobal();
default:
return -1;
}
}
/**
* Gets the width xy obj space.
*
* @return the width xy obj space
*/
private float getWidthXYLocal() {
return this.getWidthXYVectLocal().length();
}
/**
* Gets the width xy realtive to parent.
*
* @return the width xy realtive to parent
*/
private float getWidthXYRealtiveToParent() {
//This calculates the width aligned/relative to the object
Vector3D p = this.getWidthXYVectLocal();
Matrix m = new Matrix(this.peerComponent.getLocalMatrix());
m.removeTranslationFromMatrix();
p.transform(m);
return p.length();
//This calculates the dimension relative to the screen axis (here X-axis)
// Vector3D[] v = this.getVectorsRelativeToParent();
// float[] minMax = ToolsGeometry.getMinXYMaxXY(v);
// return minMax[2] - minMax[0];
}
/**
* Gets the width xy global.
*
* @return the width xy global
*/
private float getWidthXYGlobal() {
Vector3D p = this.getWidthXYVectLocal();
Matrix m = new Matrix(this.peerComponent.getGlobalMatrix());
m.removeTranslationFromMatrix();
p.transform(m);
return p.length();
// Vector3D[] v = this.getVectorsGlobal();
// float[] minMax = ToolsGeometry.getMinXYMaxXY(v);
// return minMax[2] - minMax[0];
}
/**
* Gets the "Width vector". The vector is calculated from the bounds vectors,
* representing a vector with the Width as its length in object space.
*
* @return the width xy vect obj space
*
* vector representing the Width of the boundingshape of the shape
*/
//@Override
public Vector3D getWidthXYVectLocal() {
Vector3D[] boundRectVertsLocal = this.getVectorsLocal();
Vector3D width = boundRectVertsLocal[1].getSubtracted(boundRectVertsLocal[0]);
// System.out.println("Width of " + this.getName()+ " :" + width);
return width;
}
public boolean isContainedInFrustum(IFrustum frustum) {
Vector3D[] points = this.getVectorsGlobal();
for (int i = 0; i < points.length; i++) {
Vector3D vector3D = points[i];
int test = frustum.isPointInFrustum(vector3D);
if ( test == IFrustum.INSIDE
|| test == IFrustum.INTERSECT
){
return true;
}
}
//Also check if center point is in frustum
Vector3D center = this.getCenterPointGlobal();
int test = frustum.isPointInFrustum(center);
if ( test == IFrustum.INSIDE
|| test == IFrustum.INTERSECT
){
return true;
}
return false;
}
}