package advanced.puzzle; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.mt4j.MTApplication; import org.mt4j.components.MTComponent; import org.mt4j.components.TransformSpace; import org.mt4j.components.bounds.BoundsZPlaneRectangle; import org.mt4j.components.visibleComponents.shapes.AbstractShape; import org.mt4j.components.visibleComponents.shapes.MTComplexPolygon; import org.mt4j.components.visibleComponents.shapes.MTPolygon; import org.mt4j.input.gestureAction.InertiaDragAction; import org.mt4j.input.inputProcessors.componentProcessors.dragProcessor.DragProcessor; import org.mt4j.input.inputProcessors.componentProcessors.lassoProcessor.IdragClusterable; import org.mt4j.input.inputProcessors.componentProcessors.scaleProcessor.ScaleProcessor; import org.mt4j.util.MT4jSettings; import org.mt4j.util.MTColor; import org.mt4j.util.math.ToolsMath; import org.mt4j.util.math.Vector3D; import org.mt4j.util.math.Vertex; import org.mt4j.util.opengl.GLTexture; import org.mt4j.util.xml.svg.SVGLoader; import processing.core.PApplet; import processing.core.PImage; public class PuzzleFactory { //TODO /* - show mini picture of original picture for orientation - snap pieces together? - how to break them apart again? - clusterable tiles? - if solved - set all tiles noStroke(true) to see image clearly */ public enum TileSide{ pinOut, pinIn, linear } private float tileHeight; private float tileWidth; private Vertex[] downUpOrderVerticalRightOut; private Vertex[] upDownOrderVerticalLeftOut; private Vertex[] downUpOrderVerticalLeftOut; private Vertex[] leftRightHorizontalUpOut; private Vertex[] rightLeftHorizontalUpOut; private Vertex[] rightLeftOrderHorizontalDownOut; private Vertex[] leftRightOrderHorizontalDownOut; private Vertex[] upDownOrderVerticalRightOut; private PImage image; private float horizontalTileCount; private PApplet app; private float verticalTileCount; public static String svgPath = "advanced"+MTApplication.separator+"puzzle"+MTApplication.separator+"data"+MTApplication.separator ; public static String svgname = "knobOutRight.svg"; public PuzzleFactory(MTApplication app) { this.app = app; } // public PuzzleFactory(PApplet app, float tileWidth, float tileHeight){ // // } private void init(float tileWidth, float tileHeight){ this.tileWidth = tileWidth; this.tileHeight = tileHeight; initTiles(); } private void init(PImage p, int horizontalTileCount){ // if (MT4jSettings.getInstance().isOpenGlMode() && !(p instanceof GLTexture)){ // GLTexture tex = new GLTexture(app, p); // this.image = tex; // }else{ // this.image = p; // } // // this.horizontalTileCount = horizontalTileCount; // this.verticalTileCount = horizontalTileCount; //TODO // this.tileWidth = (float)p.width/horizontalTileCount; // this.tileHeight = (float)p.height/verticalTileCount; // initTiles(); this.init(p, horizontalTileCount, horizontalTileCount); } private void init(PImage p, int horizontalTileCount, int verticalTileCount){ if (MT4jSettings.getInstance().isOpenGlMode() && !(p instanceof GLTexture)){ GLTexture tex = new GLTexture(app, p); this.image = tex; }else{ this.image = p; } this.horizontalTileCount = horizontalTileCount; this.verticalTileCount = verticalTileCount; //TODO this.tileWidth = (float)p.width/(float)horizontalTileCount; this.tileHeight = (float)p.height/(float)verticalTileCount; initTiles(); } private void initTiles(){ SVGLoader l = new SVGLoader(app); MTComponent knob = l.loadSvg(svgPath + svgname); MTPolygon knobRight = (MTPolygon) knob.getChildByIndex(0).getChildByIndex(0); knobRight.setNoFill(false); knobRight.setUseDisplayList(false); float origHeight = knobRight.getHeightXY(TransformSpace.LOCAL); //Snap to upper left 0,0 Vertex[] originalVerts = knobRight.getVerticesLocal(); originalVerts = Vertex.translateArray(originalVerts, Vector3D.ZERO_VECTOR.getSubtracted(new Vector3D(originalVerts[0]))); upDownOrderVerticalRightOut = Vertex.getDeepVertexArrayCopy(originalVerts); //Scale to desired height Vertex.scaleVectorArray(upDownOrderVerticalRightOut, Vector3D.ZERO_VECTOR, (1f/origHeight) * tileHeight, (1f/origHeight) * tileHeight, 1); downUpOrderVerticalRightOut = getInvertOrderCopy(upDownOrderVerticalRightOut); // MTPolygon p1 = new MTPolygon(getMTApplication(), downUpOrderVerticalRightOut); // getCanvas().addChild(p1); upDownOrderVerticalLeftOut = Vertex.getDeepVertexArrayCopy(upDownOrderVerticalRightOut); Vertex.scaleVectorArray(upDownOrderVerticalLeftOut, new Vector3D(0,origHeight/2f), -1, 1, 1); // MTPolygon p2 = new MTPolygon(getMTApplication(), vertsVerticalLeftOut); // getCanvas().addChild(p2); downUpOrderVerticalLeftOut = getInvertOrderCopy(upDownOrderVerticalLeftOut); leftRightHorizontalUpOut = Vertex.getDeepVertexArrayCopy(originalVerts); Vertex.rotateZVectorArray(leftRightHorizontalUpOut, Vector3D.ZERO_VECTOR, -90); //Scale to desired width Vertex.scaleVectorArray(leftRightHorizontalUpOut, Vector3D.ZERO_VECTOR, (1f/origHeight) * tileWidth, (1f/origHeight) * tileWidth, 1); // MTPolygon p3 = new MTPolygon(getMTApplication(), leftRightHorizontalUpOut); // getCanvas().addChild(p3); rightLeftHorizontalUpOut = getInvertOrderCopy(leftRightHorizontalUpOut); leftRightOrderHorizontalDownOut = Vertex.getDeepVertexArrayCopy(leftRightHorizontalUpOut); Vertex.scaleVectorArray(leftRightOrderHorizontalDownOut, new Vector3D(origHeight/2f,0), 1, -1, 1); // MTPolygon p4 = new MTPolygon(getMTApplication(), leftRightOrderHorizontalDownOut); // getCanvas().addChild(p4); rightLeftOrderHorizontalDownOut = getInvertOrderCopy(leftRightOrderHorizontalDownOut); } public AbstractShape[] createTiles(PImage p, int horizontalTileCount){ return createTiles(p, horizontalTileCount, horizontalTileCount); } public AbstractShape[] createTiles(PImage p, int horizontalTileCount, int verticalTileCount){ this.init(p, horizontalTileCount, verticalTileCount); List<AbstractShape> tiles = new ArrayList<AbstractShape>(); TileSide[] sides = new TileSide[]{TileSide.pinIn, TileSide.pinOut}; for (int i = 0; i < verticalTileCount; i++) { for (int j = 0; j < horizontalTileCount; j++) { TileSide top = TileSide.pinOut, right = TileSide.pinOut, bottom = TileSide.pinOut, left = TileSide.pinIn; //left und top have to be checked against the previous tiles, right and bottom can be random (if not linear) right = sides[Math.round(ToolsMath.getRandom(0, sides.length-1))]; bottom = sides[Math.round(ToolsMath.getRandom(0, sides.length-1))]; if (j == 0){ //Left side has to be linear left = TileSide.linear; if (i == 0){ //top side has to be linear top = TileSide.linear; // left = getFittingTileSideTo(getRightOfLeftTile(tiles, i, j)); }else if (i == verticalTileCount -1){ //Bottom side has to be linear bottom = TileSide.linear; top = getFittingTileSideTo(getBottomOfUpperTile(tiles, i, j)); // left = getFittingTileSideTo(getRightOfLeftTile(tiles, i, j)); }else{ //in a middle vetical - up or bottom side have to have a pin top = getFittingTileSideTo(getBottomOfUpperTile(tiles, i, j)); // left = getFittingTileSideTo(getRightOfLeftTile(tiles, i, j)); } }else if (j == horizontalTileCount -1){ right = TileSide.linear; //Right side has to be linear if (i == 0){ //top side has to be linear top = TileSide.linear; left = getFittingTileSideTo(getRightOfLeftTile(tiles, i, j)); }else if (i == verticalTileCount -1){ //Bottom side has to be linear bottom = TileSide.linear; top = getFittingTileSideTo(getBottomOfUpperTile(tiles, i, j)); left = getFittingTileSideTo(getRightOfLeftTile(tiles, i, j)); }else{ //in a middle vetical - up or bottom side have to have a pin top = getFittingTileSideTo(getBottomOfUpperTile(tiles, i, j)); left = getFittingTileSideTo(getRightOfLeftTile(tiles, i, j)); } }else{ //in a middle horizontal, left or right side have to have a pin if (i == 0){ //top side has to be linear top = TileSide.linear; left = getFittingTileSideTo(getRightOfLeftTile(tiles, i, j)); }else if (i == verticalTileCount -1){ //Bottom side has to be linear bottom = TileSide.linear; top = getFittingTileSideTo(getBottomOfUpperTile(tiles, i, j)); left = getFittingTileSideTo(getRightOfLeftTile(tiles, i, j)); }else{ //in a middle vetical - up or bottom side have to have a pin top = getFittingTileSideTo(getBottomOfUpperTile(tiles, i, j)); left = getFittingTileSideTo(getRightOfLeftTile(tiles, i, j)); } } MTComplexPolygon tile = getPolygon(app, top, right, bottom, left, this.tileWidth, this.tileHeight); tile.setName(i + "" + j); tile.setUserData("i", i); tile.setUserData("j", j); tile.setUserData("top", top); tile.setUserData("right", right); tile.setUserData("bottom", bottom); tile.setUserData("left", left); //Create some default texture coords tile.setBounds(new BoundsZPlaneRectangle(tile)); if (tile != null && tile.hasBounds() && tile.getBounds() instanceof BoundsZPlaneRectangle){ BoundsZPlaneRectangle bounds = (BoundsZPlaneRectangle) tile.getBounds(); // float width = bounds.getWidthXY(TransformSpace.LOCAL); // float height = bounds.getHeightXY(TransformSpace.LOCAL); // float upperLeftX = bounds.getVectorsLocal()[0].x; // float upperLeftY = bounds.getVectorsLocal()[0].y; // float upperLeftX = bounds.getVectorsLocal()[0].x + j* tileWidth ; // float upperLeftY = bounds.getVectorsLocal()[0].y + i * tileHeight; Vertex[] verts = tile.getVerticesLocal(); for (int n = 0; n < verts.length; n++) { Vertex vertex = verts[n]; // vertex.setTexCoordU((vertex.x-upperLeftX )/width); // vertex.setTexCoordV((vertex.y-upperLeftY)/height); // vertex.setTexCoordU((vertex.x - upperLeftX + (j * tileWidth)) / p.width); // vertex.setTexCoordV((vertex.y - upperLeftY + (i * tileHeight)) / p.height); vertex.setTexCoordU((vertex.x + (j * tileWidth)) / p.width); vertex.setTexCoordV((vertex.y + (i * tileHeight)) / p.height); //System.out.println("TexU:" + vertex.getTexCoordU() + " TexV:" + vertex.getTexCoordV()); } tile.getGeometryInfo().updateTextureBuffer(tile.isUseVBOs()); //Set the texture tile.setTexture(p); // tile.setNoStroke(true); // tile.setStrokeColor(MTColor.GREY); tile.setStrokeColor(new MTColor(80,80,80)); tile.setStrokeWeight(0.7f); tiles.add(tile); } } } return tiles.toArray(new AbstractShape[tiles.size()]); } private TileSide getBottomOfUpperTile(List<AbstractShape> list, int currentI, int currentJ){ if (currentI-1 < 0){ return TileSide.linear; } for (Iterator<AbstractShape> iterator = list.iterator(); iterator.hasNext();) { AbstractShape tile = (AbstractShape) iterator.next(); int i = (Integer) tile.getUserData("i"); int j = (Integer) tile.getUserData("j"); if (i == currentI -1 && j == currentJ){ return (TileSide) tile.getUserData("bottom"); } } return TileSide.linear; } private TileSide getRightOfLeftTile(List<AbstractShape> list, int currentI, int currentJ){ if (currentJ-1 < 0){ return TileSide.linear; } for (Iterator<AbstractShape> iterator = list.iterator(); iterator.hasNext();) { AbstractShape tile = (AbstractShape) iterator.next(); int i = (Integer) tile.getUserData("i"); int j = (Integer) tile.getUserData("j"); if (i == currentI && j == currentJ-1){ return (TileSide) tile.getUserData("right"); } } return TileSide.linear; } private TileSide getFittingTileSideTo(TileSide otherSide){ TileSide fitting = TileSide.linear; switch (otherSide) { case linear: fitting = TileSide.linear; break; case pinIn: fitting = TileSide.pinOut; break; case pinOut: fitting = TileSide.pinIn; break; default: break; } return fitting; } public MTComplexPolyClusterable getPolygon(PApplet app, TileSide top, TileSide right, TileSide bottom, TileSide left, float tileWidth, float tileHeight){ this.init(tileWidth, tileHeight); Vertex[] v = getTile(top, right, bottom, left); MTComplexPolyClusterable poly = new MTComplexPolyClusterable(app, v); poly.removeAllGestureEventListeners(ScaleProcessor.class); poly.addGestureListener(DragProcessor.class, new InertiaDragAction()); return poly; } private class MTComplexPolyClusterable extends MTComplexPolygon implements IdragClusterable{ public MTComplexPolyClusterable(PApplet app, Vertex[] vertices) { super(app, vertices); } public boolean isSelected() { return false; } public void setSelected(boolean selected) { } } private Vertex[] getTile(TileSide top, TileSide right, TileSide bottom, TileSide left){ List<Vertex> list = new ArrayList<Vertex>(); switch (top) { case linear: list.add(new Vertex(0,0)); list.add(new Vertex(tileWidth, 0)); break; case pinIn: addAll(Vertex.getDeepVertexArrayCopy(leftRightOrderHorizontalDownOut), list); break; case pinOut: addAll(Vertex.getDeepVertexArrayCopy(leftRightHorizontalUpOut), list); break; default: break; } switch (right) { case linear: // list.add(new Vertex(tileWidth,0)); list.add(new Vertex(tileWidth, tileHeight)); break; case pinIn: addAll(getCopyOffset(this.upDownOrderVerticalLeftOut, tileWidth, 0), list); break; case pinOut: addAll(getCopyOffset(this.upDownOrderVerticalRightOut, tileWidth, 0), list); break; default: break; } switch (bottom) { case linear: // list.add(new Vertex(tileWidth, tileHeight)); list.add(new Vertex(0, tileHeight)); break; case pinIn: addAll(getCopyOffset(this.rightLeftHorizontalUpOut, 0, tileHeight), list); break; case pinOut: addAll(getCopyOffset(this.rightLeftOrderHorizontalDownOut, 0, tileHeight), list); break; default: break; } switch (left) { case linear: // list.add(new Vertex(0, tileHeight)); list.add(new Vertex(0, 0)); break; case pinIn: addAll(Vertex.getDeepVertexArrayCopy(this.downUpOrderVerticalRightOut), list); break; case pinOut: addAll(Vertex.getDeepVertexArrayCopy(this.downUpOrderVerticalLeftOut), list); break; default: break; } return list.toArray(new Vertex[list.size()]); } private void addAll(Vertex[] vertices, List<Vertex> list){ for (int i = 0; i < vertices.length; i++) { Vertex vertex = vertices[i]; list.add(vertex); } } private Vertex[] getCopyOffset(Vertex[] verts, float xOffset, float yOffset){ Vertex[] copy = new Vertex[verts.length]; // Vertex[] copy = Vertex.getDeepVertexArrayCopy(verts); for (int i = 0; i < copy.length; i++) { copy[i] = (Vertex) new Vertex(verts[i]).addLocal(new Vertex(xOffset, yOffset)); } return copy; } private Vertex[] getInvertOrderCopyOffset(Vertex[] verts, float xOffset, float yOffset){ Vertex[] copy = new Vertex[verts.length]; // Vertex[] copy = Vertex.getDeepVertexArrayCopy(verts); for (int i = 0; i < copy.length; i++) { copy[i] = (Vertex) new Vertex(verts[verts.length -i -1]).addLocal(new Vertex(xOffset, yOffset)); } return copy; } private Vertex[] getInvertOrderCopy(Vertex[] verts){ Vertex[] copy = new Vertex[verts.length]; // Vertex[] copy = Vertex.getDeepVertexArrayCopy(verts); for (int i = 0; i < verts.length; i++) { // copy[i] = copy[copy.length -i -1]; copy[i] = new Vertex(verts[verts.length -i -1]); } return copy; } }