/*********************************************************************** * 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.clusters; import java.util.ArrayList; import org.mt4j.components.MTComponent; import org.mt4j.components.TransformSpace; import org.mt4j.components.bounds.BoundsArbitraryPlanarPolygon; import org.mt4j.components.bounds.BoundsZPlaneRectangle; import org.mt4j.components.visibleComponents.shapes.AbstractShape; import org.mt4j.components.visibleComponents.shapes.MTPolygon; import org.mt4j.util.math.ConvexQuickHull2D; import org.mt4j.util.math.Matrix; import org.mt4j.util.math.Vector3D; import org.mt4j.util.math.Vertex; import processing.core.PApplet; /** * The Class Cluster. * @author Christopher Ruff */ public class Cluster extends MTComponent { //extends MTComponent/implements IMTComponent3D? /** The selection polygon. */ private MTPolygon selectionPolygon; //FIXME THIS WHOLE CLASS SHOULD BE RE-WORKED! //Disadvantages of the cluster: //each of the children are transformed, not only the cluster parent -> performance.. //hardwired into mtcanvas -> should be removed.. //could be replaced with a composite component -> but then we have the problem of picking child components of the group /** * Instantiates a new cluster. * * @param pApplet the applet * @param selectionPolygon the selection polygon */ public Cluster(PApplet pApplet,MTPolygon selectionPolygon) { this(pApplet, new MTComponent[]{}, selectionPolygon); } /** * Info: Cluster can only hold instances of MTBaseComponent!. * * @param pApplet the applet * @param components the components * @param selectionPolygon the selection polygon */ public Cluster(PApplet pApplet, MTComponent[] components, MTPolygon selectionPolygon) { super(pApplet); this.selectionPolygon = selectionPolygon; if (selectionPolygon != null){ this.addChild(selectionPolygon); } for (int i = 0; i < components.length; i++) { MTComponent component3D = components[i]; this.addChild(component3D); } this.setName("unnamed Cluster"); } /** * Gets the cluster polygon. * * @return the cluster polygon */ public MTPolygon getClusterPolygon() { return selectionPolygon; } /** * Calculates the convex hull of all its children. * Then changes the cluster-polygon to represent that convex hull * and adds it as a child. */ public void packClusterPolygon(){ ArrayList<Vector3D> allClusteredVerts = new ArrayList<Vector3D>(); int shapes = 0; //Remove the old clusterpoly MTPolygon clusterPoly = getClusterPolygon(); this.removeChild(clusterPoly); MTComponent[] children = this.getChildren(); for (int i = 0; i < children.length; i++) { MTComponent component = children[i]; //Get vertices for convex hull of all selected components if (component instanceof AbstractShape){ shapes++; AbstractShape shape = (AbstractShape)component; // Vertex[] verts = shape.getVerticesPickingWorld(); Vector3D[] verts = null; // if (shape.isBoundingShapeSet()){ // verts = shape.getBoundingShape().getVectorsGlobal(); // }else{ // verts = shape.getVerticesGlobal(); // } if (shape.hasBounds()){ if (shape.getBounds() instanceof BoundsZPlaneRectangle || shape.getBounds() instanceof BoundsArbitraryPlanarPolygon){ verts = shape.getBounds().getVectorsGlobal(); }else{ BoundsZPlaneRectangle b = new BoundsZPlaneRectangle(shape); verts = b.getVectorsGlobal(); } }else{ BoundsZPlaneRectangle b = new BoundsZPlaneRectangle(shape); verts = b.getVectorsGlobal(); // verts = shape.getVerticesGlobal(); } for (Vector3D v : verts){ allClusteredVerts.add(v); } } } if (shapes != 0 && shapes == children.length){ //If all children are of type abstractShape ArrayList<Vector3D> hull = ConvexQuickHull2D.getConvexHull2D(allClusteredVerts); if (hull.size() > 0){ //Correctly close polygon with 1.st vertex again hull.add(hull.get(0).getCopy()); Vertex[] newVerts = new Vertex[hull.size()]; for (int i = 0; i < hull.size(); i++) { Vector3D vec = hull.get(i); newVerts[i] = new Vertex(vec); } // Vertex[] newVerts = (Vertex[])hull.toArray(new Vertex[hull.size()]); // System.out.println("Hull vertices: "); for (Vertex v : newVerts){ v.setRGBA(100,150,250, 50); } clusterPoly.setVertices(newVerts); clusterPoly.setBoundsBehaviour(AbstractShape.BOUNDS_DONT_USE); // clusterPoly.setBoundingShape(new BoundsArbitraryPlanarPolygon(clusterPoly, clusterPoly.getVerticesLocal())); //Reset matrix of the clusterpoly because the new vertices are set at the global location clusterPoly.setLocalMatrix(new Matrix()); //FIXME center are is negative if verts are in counterclockwise order? // Vector3D clusterCenter = clusterPoly.getCenterPointGlobal(); // clusterPoly.scaleGlobal(1.1f, 1.1f, 1, new Vector3D(-1* clusterCenter.x, -1 * clusterCenter.y, clusterCenter.z)); clusterPoly.scale(1.1f, 1.1f, 1, clusterPoly.getCenterPointLocal(), TransformSpace.LOCAL); this.addChild(clusterPoly); }else{ System.err.println("Couldnt pack polygon."); } } } /** * overridden to only put the cluster polygon last * in its parent list. */ @Override public void sendToFront() { if (this.getClusterPolygon() != null){ this.getClusterPolygon().sendToFront(); } for (int i = 0; i < this.getChildren().length; i++) { MTComponent childComp = this.getChildren()[i]; if (!childComp.equals(this.getClusterPolygon())) childComp.sendToFront(); } } @Override public void addChild(int i, MTComponent tangibleComp) { //Overridden, so the component keeps it original parent this.getChildList().add(i, tangibleComp); } @Override public void addChild(MTComponent tangibleComp) { this.getChildList().add(tangibleComp); } @Override public void addChildren(MTComponent[] tangibleComps) { for (int i = 0; i < tangibleComps.length; i++) { MTComponent object = tangibleComps[i]; //Add direct objects this.getChildList().add(object); } } @Override public void removeAllChildren() { this.getChildList().clear(); } @Override public void removeChild(int i) { this.getChildList().remove(i); } @Override public void removeChild(MTComponent comp) { this.getChildList().remove(comp); } //TODO GANZE CLUSTERING �BERARBEITEN //SO GIBTS PROBLEME WENN GECLUSTERTE OBJECTE IN VERSCHIEDENEN PARENTS SIND // => DANN WERDEN SIE VERSCHIEDEN TRANSFORMIERT // aM BESTEN ALLE ALS KINDER DES CLUSTERPOLYS MACHEN UND DAS AUF COMPOSITE=TRUE MACHEN // ABER WOHIN GEHEN SIE WENN SIE AUS DEM CLUSTER GEL�ST WERDEN? VIELLEICHT GIBTS DIE ALTE GRUPPE NICHT MEHR? // DANN AUF DEN WORLD CANVAS SETZEN; => ABER AN GLEICHER STELLE! /** * Transforms the shapes local coordinate space by the given matrix. * * @param transformMatrix the transform matrix */ public void transform(Matrix transformMatrix) { for (MTComponent c : this.getChildList()){ c.transform(transformMatrix); } } public void translateGlobal(Vector3D dirVect) { for (MTComponent c : this.getChildList()){ c.translateGlobal(dirVect); } } public void translate(Vector3D dirVect) { for (MTComponent c : this.getChildList()){ c.translate(dirVect); } } public void rotateXGlobal(Vector3D rotationPoint, float degree) { for (MTComponent c : this.getChildList()){ c.rotateXGlobal(rotationPoint, degree); } } public void rotateX(Vector3D rotationPoint, float degree) { for (MTComponent c : this.getChildList()){ c.rotateX(rotationPoint, degree); } } public void rotateYGlobal(Vector3D rotationPoint, float degree) { for (MTComponent c : this.getChildList()){ c.rotateYGlobal(rotationPoint, degree); } } public void rotateY(Vector3D rotationPoint, float degree) { for (MTComponent c : this.getChildList()){ c.rotateY(rotationPoint, degree); } } public void rotateZGlobal(Vector3D rotationPoint, float degree) { for (MTComponent c : this.getChildList()){ c.rotateZGlobal(rotationPoint, degree); } } public void rotateZ(Vector3D rotationPoint, float degree) { for (MTComponent c : this.getChildList()){ c.rotateZ(rotationPoint, degree); } } public void scaleGlobal(float factor, Vector3D scaleReferencePoint) { this.scaleGlobal(factor, factor, factor, scaleReferencePoint); } /** * scales the polygon around the scalingPoint, currently dosent support scaling around the Z axis. * * @param X the x * @param Y the y * @param Z the z * @param scalingPoint the scaling point */ public void scaleGlobal(float X, float Y, float Z, Vector3D scalingPoint) { for (MTComponent c : this.getChildList()){ c.scaleGlobal(X, Y, Z, scalingPoint); } } public void scale(float factor, Vector3D scaleReferencePoint) { this.scale(factor, factor, factor, scaleReferencePoint); } /** * scales the polygon around the scalingPoint, currently dosent support scaling around the Z axis. * * @param X the x * @param Y the y * @param Z the z * @param scalingPoint the scaling point */ public void scale(float X, float Y, float Z, Vector3D scalingPoint) { for (MTComponent c : this.getChildList()){ c.scale(X, Y, Z, scalingPoint); } } }