/examples/advanced/puzzle/PuzzleFactory.java

http://mt4j.googlecode.com/ · Java · 519 lines · 369 code · 77 blank · 73 comment · 49 complexity · 7e222085732de38cf9f6fb4452a3e1ad MD5 · raw file

  1. package advanced.puzzle;
  2. import java.util.ArrayList;
  3. import java.util.List;
  4. import org.mt4j.MTApplication;
  5. import org.mt4j.components.MTComponent;
  6. import org.mt4j.components.TransformSpace;
  7. import org.mt4j.components.bounds.BoundsZPlaneRectangle;
  8. import org.mt4j.components.interfaces.IMTComponent3D;
  9. import org.mt4j.components.visibleComponents.shapes.AbstractShape;
  10. import org.mt4j.components.visibleComponents.shapes.MTComplexPolygon;
  11. import org.mt4j.components.visibleComponents.shapes.MTPolygon;
  12. import org.mt4j.input.gestureAction.InertiaDragAction;
  13. import org.mt4j.input.inputData.InputCursor;
  14. import org.mt4j.input.inputProcessors.IGestureEventListener;
  15. import org.mt4j.input.inputProcessors.MTGestureEvent;
  16. import org.mt4j.input.inputProcessors.componentProcessors.dragProcessor.DragProcessor;
  17. import org.mt4j.input.inputProcessors.componentProcessors.lassoProcessor.IdragClusterable;
  18. import org.mt4j.input.inputProcessors.componentProcessors.rotateProcessor.RotateEvent;
  19. import org.mt4j.input.inputProcessors.componentProcessors.rotateProcessor.RotateProcessor;
  20. import org.mt4j.input.inputProcessors.componentProcessors.scaleProcessor.ScaleProcessor;
  21. import org.mt4j.util.MT4jSettings;
  22. import org.mt4j.util.MTColor;
  23. import org.mt4j.util.math.Tools3D;
  24. import org.mt4j.util.math.ToolsGeometry;
  25. import org.mt4j.util.math.ToolsMath;
  26. import org.mt4j.util.math.Vector3D;
  27. import org.mt4j.util.math.Vertex;
  28. import org.mt4j.util.opengl.GLTexture;
  29. import org.mt4j.util.xml.svg.SVGLoader;
  30. import processing.core.PApplet;
  31. import processing.core.PImage;
  32. public class PuzzleFactory {
  33. //TODO
  34. /*
  35. - show mini picture of original picture for orientation
  36. - snap pieces together? - how to break them apart again?
  37. - clusterable tiles?
  38. - if solved - set all tiles noStroke(true) to see image clearly
  39. */
  40. public enum TileSide{
  41. pinOut,
  42. pinIn,
  43. linear
  44. }
  45. private float tileHeight;
  46. private float tileWidth;
  47. private Vertex[] downUpOrderVerticalRightOut;
  48. private Vertex[] upDownOrderVerticalLeftOut;
  49. private Vertex[] downUpOrderVerticalLeftOut;
  50. private Vertex[] leftRightHorizontalUpOut;
  51. private Vertex[] rightLeftHorizontalUpOut;
  52. private Vertex[] rightLeftOrderHorizontalDownOut;
  53. private Vertex[] leftRightOrderHorizontalDownOut;
  54. private Vertex[] upDownOrderVerticalRightOut;
  55. private PImage image;
  56. private float horizontalTileCount;
  57. private PApplet app;
  58. private float verticalTileCount;
  59. public static String svgPath = "advanced"+MTApplication.separator+"puzzle"+MTApplication.separator+"data"+MTApplication.separator ;
  60. public static String svgname = "knobOutRight.svg";
  61. public PuzzleFactory(MTApplication app) {
  62. this.app = app;
  63. }
  64. // public PuzzleFactory(PApplet app, float tileWidth, float tileHeight){
  65. //
  66. // }
  67. private void init(float tileWidth, float tileHeight){
  68. this.tileWidth = tileWidth;
  69. this.tileHeight = tileHeight;
  70. initTiles();
  71. }
  72. private void init(PImage p, int horizontalTileCount){
  73. // if (MT4jSettings.getInstance().isOpenGlMode() && !(p instanceof GLTexture)){
  74. // GLTexture tex = new GLTexture(app, p);
  75. // this.image = tex;
  76. // }else{
  77. // this.image = p;
  78. // }
  79. //
  80. // this.horizontalTileCount = horizontalTileCount;
  81. // this.verticalTileCount = horizontalTileCount; //TODO
  82. // this.tileWidth = (float)p.width/horizontalTileCount;
  83. // this.tileHeight = (float)p.height/verticalTileCount;
  84. // initTiles();
  85. this.init(p, horizontalTileCount, horizontalTileCount);
  86. }
  87. private void init(PImage p, int horizontalTileCount, int verticalTileCount){
  88. if (MT4jSettings.getInstance().isOpenGlMode() && !(p instanceof GLTexture)){
  89. GLTexture tex = new GLTexture(app, p);
  90. this.image = tex;
  91. }else{
  92. this.image = p;
  93. }
  94. this.horizontalTileCount = horizontalTileCount;
  95. this.verticalTileCount = verticalTileCount; //TODO
  96. this.tileWidth = (float)p.width/(float)horizontalTileCount;
  97. this.tileHeight = (float)p.height/(float)verticalTileCount;
  98. initTiles();
  99. }
  100. private void initTiles(){
  101. SVGLoader l = new SVGLoader(app);
  102. MTComponent knob = l.loadSvg(svgPath + svgname);
  103. MTPolygon knobRight = (MTPolygon) knob.getChildByIndex(0).getChildByIndex(0);
  104. knobRight.setNoFill(false);
  105. knobRight.setUseDisplayList(false);
  106. float origHeight = knobRight.getHeightXY(TransformSpace.LOCAL);
  107. //Snap to upper left 0,0
  108. Vertex[] originalVerts = knobRight.getVerticesLocal();
  109. originalVerts = Vertex.translateArray(originalVerts, Vector3D.ZERO_VECTOR.getSubtracted(new Vector3D(originalVerts[0])));
  110. upDownOrderVerticalRightOut = Vertex.getDeepVertexArrayCopy(originalVerts);
  111. //Scale to desired height
  112. Vertex.scaleVectorArray(upDownOrderVerticalRightOut, Vector3D.ZERO_VECTOR, (1f/origHeight) * tileHeight, (1f/origHeight) * tileHeight, 1);
  113. downUpOrderVerticalRightOut = getInvertOrderCopy(upDownOrderVerticalRightOut);
  114. // MTPolygon p1 = new MTPolygon(getMTApplication(), downUpOrderVerticalRightOut);
  115. // getCanvas().addChild(p1);
  116. upDownOrderVerticalLeftOut = Vertex.getDeepVertexArrayCopy(upDownOrderVerticalRightOut);
  117. Vertex.scaleVectorArray(upDownOrderVerticalLeftOut, new Vector3D(0,origHeight/2f), -1, 1, 1);
  118. // MTPolygon p2 = new MTPolygon(getMTApplication(), vertsVerticalLeftOut);
  119. // getCanvas().addChild(p2);
  120. downUpOrderVerticalLeftOut = getInvertOrderCopy(upDownOrderVerticalLeftOut);
  121. leftRightHorizontalUpOut = Vertex.getDeepVertexArrayCopy(originalVerts);
  122. Vertex.rotateZVectorArray(leftRightHorizontalUpOut, Vector3D.ZERO_VECTOR, -90);
  123. //Scale to desired width
  124. Vertex.scaleVectorArray(leftRightHorizontalUpOut, Vector3D.ZERO_VECTOR, (1f/origHeight) * tileWidth, (1f/origHeight) * tileWidth, 1);
  125. // MTPolygon p3 = new MTPolygon(getMTApplication(), leftRightHorizontalUpOut);
  126. // getCanvas().addChild(p3);
  127. rightLeftHorizontalUpOut = getInvertOrderCopy(leftRightHorizontalUpOut);
  128. leftRightOrderHorizontalDownOut = Vertex.getDeepVertexArrayCopy(leftRightHorizontalUpOut);
  129. Vertex.scaleVectorArray(leftRightOrderHorizontalDownOut, new Vector3D(origHeight/2f,0), 1, -1, 1);
  130. // MTPolygon p4 = new MTPolygon(getMTApplication(), leftRightOrderHorizontalDownOut);
  131. // getCanvas().addChild(p4);
  132. rightLeftOrderHorizontalDownOut = getInvertOrderCopy(leftRightOrderHorizontalDownOut);
  133. }
  134. public AbstractShape[] createTiles(PImage p, int horizontalTileCount){
  135. return createTiles(p, horizontalTileCount, horizontalTileCount);
  136. }
  137. public AbstractShape[] createTiles(PImage p, int horizontalTileCount, int verticalTileCount){
  138. this.init(p, horizontalTileCount, verticalTileCount);
  139. List<AbstractShape> tiles = new ArrayList<AbstractShape>();
  140. TileSide[] sides = new TileSide[]{TileSide.pinIn, TileSide.pinOut};
  141. for (int i = 0; i < verticalTileCount; i++) {
  142. for (int j = 0; j < horizontalTileCount; j++) {
  143. TileSide top = TileSide.pinOut, right = TileSide.pinOut, bottom = TileSide.pinOut, left = TileSide.pinIn;
  144. //left und top have to be checked against the previous tiles, right and bottom can be random (if not linear)
  145. right = sides[Math.round(ToolsMath.getRandom(0, sides.length-1))];
  146. bottom = sides[Math.round(ToolsMath.getRandom(0, sides.length-1))];
  147. if (j == 0){
  148. //Left side has to be linear
  149. left = TileSide.linear;
  150. if (i == 0){
  151. //top side has to be linear
  152. top = TileSide.linear;
  153. // left = getFittingTileSideTo(getRightOfLeftTile(tiles, i, j));
  154. }else if (i == verticalTileCount -1){
  155. //Bottom side has to be linear
  156. bottom = TileSide.linear;
  157. top = getFittingTileSideTo(getBottomOfUpperTile(tiles, i, j));
  158. // left = getFittingTileSideTo(getRightOfLeftTile(tiles, i, j));
  159. }else{
  160. //in a middle vetical - up or bottom side have to have a pin
  161. top = getFittingTileSideTo(getBottomOfUpperTile(tiles, i, j));
  162. // left = getFittingTileSideTo(getRightOfLeftTile(tiles, i, j));
  163. }
  164. }else if (j == horizontalTileCount -1){
  165. right = TileSide.linear;
  166. //Right side has to be linear
  167. if (i == 0){
  168. //top side has to be linear
  169. top = TileSide.linear;
  170. left = getFittingTileSideTo(getRightOfLeftTile(tiles, i, j));
  171. }else if (i == verticalTileCount -1){
  172. //Bottom side has to be linear
  173. bottom = TileSide.linear;
  174. top = getFittingTileSideTo(getBottomOfUpperTile(tiles, i, j));
  175. left = getFittingTileSideTo(getRightOfLeftTile(tiles, i, j));
  176. }else{
  177. //in a middle vetical - up or bottom side have to have a pin
  178. top = getFittingTileSideTo(getBottomOfUpperTile(tiles, i, j));
  179. left = getFittingTileSideTo(getRightOfLeftTile(tiles, i, j));
  180. }
  181. }else{
  182. //in a middle horizontal, left or right side have to have a pin
  183. if (i == 0){
  184. //top side has to be linear
  185. top = TileSide.linear;
  186. left = getFittingTileSideTo(getRightOfLeftTile(tiles, i, j));
  187. }else if (i == verticalTileCount -1){
  188. //Bottom side has to be linear
  189. bottom = TileSide.linear;
  190. top = getFittingTileSideTo(getBottomOfUpperTile(tiles, i, j));
  191. left = getFittingTileSideTo(getRightOfLeftTile(tiles, i, j));
  192. }else{
  193. //in a middle vetical - up or bottom side have to have a pin
  194. top = getFittingTileSideTo(getBottomOfUpperTile(tiles, i, j));
  195. left = getFittingTileSideTo(getRightOfLeftTile(tiles, i, j));
  196. }
  197. }
  198. MTComplexPolygon tile = getPolygon(app, top, right, bottom, left, this.tileWidth, this.tileHeight);
  199. tile.setName(i + "" + j);
  200. tile.setUserData("i", i);
  201. tile.setUserData("j", j);
  202. tile.setUserData("top", top);
  203. tile.setUserData("right", right);
  204. tile.setUserData("bottom", bottom);
  205. tile.setUserData("left", left);
  206. //Create some default texture coords
  207. tile.setBounds(new BoundsZPlaneRectangle(tile));
  208. if (tile != null && tile.hasBounds() && tile.getBounds() instanceof BoundsZPlaneRectangle){
  209. BoundsZPlaneRectangle bounds = (BoundsZPlaneRectangle) tile.getBounds();
  210. // float width = bounds.getWidthXY(TransformSpace.LOCAL);
  211. // float height = bounds.getHeightXY(TransformSpace.LOCAL);
  212. // float upperLeftX = bounds.getVectorsLocal()[0].x;
  213. // float upperLeftY = bounds.getVectorsLocal()[0].y;
  214. // float upperLeftX = bounds.getVectorsLocal()[0].x + j* tileWidth ;
  215. // float upperLeftY = bounds.getVectorsLocal()[0].y + i * tileHeight;
  216. Vertex[] verts = tile.getVerticesLocal();
  217. for (Vertex vertex : verts) {
  218. // vertex.setTexCoordU((vertex.x-upperLeftX )/width);
  219. // vertex.setTexCoordV((vertex.y-upperLeftY)/height);
  220. // vertex.setTexCoordU((vertex.x - upperLeftX + (j * tileWidth)) / p.width);
  221. // vertex.setTexCoordV((vertex.y - upperLeftY + (i * tileHeight)) / p.height);
  222. vertex.setTexCoordU((vertex.x + (j * tileWidth)) / p.width);
  223. vertex.setTexCoordV((vertex.y + (i * tileHeight)) / p.height);
  224. //System.out.println("TexU:" + vertex.getTexCoordU() + " TexV:" + vertex.getTexCoordV());
  225. }
  226. tile.getGeometryInfo().updateTextureBuffer(tile.isUseVBOs());
  227. //Set the texture
  228. tile.setTexture(p);
  229. // tile.setNoStroke(true);
  230. // tile.setStrokeColor(MTColor.GREY);
  231. tile.setStrokeColor(new MTColor(80,80,80));
  232. tile.setStrokeWeight(0.7f);
  233. tiles.add(tile);
  234. }
  235. }
  236. }
  237. return tiles.toArray(new AbstractShape[tiles.size()]);
  238. }
  239. private TileSide getBottomOfUpperTile(List<AbstractShape> list, int currentI, int currentJ){
  240. if (currentI-1 < 0){
  241. return TileSide.linear;
  242. }
  243. for (AbstractShape tile : list) {
  244. int i = (Integer) tile.getUserData("i");
  245. int j = (Integer) tile.getUserData("j");
  246. if (i == currentI - 1 && j == currentJ) {
  247. return (TileSide) tile.getUserData("bottom");
  248. }
  249. }
  250. return TileSide.linear;
  251. }
  252. private TileSide getRightOfLeftTile(List<AbstractShape> list, int currentI, int currentJ){
  253. if (currentJ-1 < 0){
  254. return TileSide.linear;
  255. }
  256. for (AbstractShape tile : list) {
  257. int i = (Integer) tile.getUserData("i");
  258. int j = (Integer) tile.getUserData("j");
  259. if (i == currentI && j == currentJ - 1) {
  260. return (TileSide) tile.getUserData("right");
  261. }
  262. }
  263. return TileSide.linear;
  264. }
  265. private TileSide getFittingTileSideTo(TileSide otherSide){
  266. TileSide fitting = TileSide.linear;
  267. switch (otherSide) {
  268. case linear:
  269. fitting = TileSide.linear;
  270. break;
  271. case pinIn:
  272. fitting = TileSide.pinOut;
  273. break;
  274. case pinOut:
  275. fitting = TileSide.pinIn;
  276. break;
  277. default:
  278. break;
  279. }
  280. return fitting;
  281. }
  282. public MTComplexPolyClusterable getPolygon(final PApplet app, TileSide top, TileSide right, TileSide bottom, TileSide left, float tileWidth, float tileHeight){
  283. this.init(tileWidth, tileHeight);
  284. Vertex[] v = getTile(top, right, bottom, left);
  285. MTComplexPolyClusterable poly = new MTComplexPolyClusterable(app, v);
  286. poly.removeAllGestureEventListeners(ScaleProcessor.class);
  287. poly.addGestureListener(DragProcessor.class, new InertiaDragAction());
  288. //FIXME TEST
  289. poly.removeAllGestureEventListeners(RotateProcessor.class);
  290. poly.addGestureListener(RotateProcessor.class, new RotationListener(poly));
  291. return poly;
  292. }
  293. //FIXME TEST
  294. private class RotationListener implements IGestureEventListener{
  295. Vector3D startP1;
  296. InputCursor oldC1;
  297. InputCursor oldC2;
  298. Vector3D planeNormal;
  299. private Vector3D lastMiddle;
  300. public RotationListener(IMTComponent3D comp){
  301. planeNormal = new Vector3D(0,0,1);
  302. }
  303. public boolean processGestureEvent(MTGestureEvent ge) {
  304. IMTComponent3D comp = ge.getTarget();
  305. RotateEvent re = (RotateEvent)ge;
  306. float deg = re.getRotationDegrees();
  307. InputCursor c1 = re.getFirstCursor();
  308. InputCursor c2 = re.getSecondCursor();
  309. switch (re.getId()) {
  310. case RotateEvent.GESTURE_STARTED:{
  311. oldC1 = c1;
  312. oldC2 = c2;
  313. startP1 = comp.getIntersectionGlobal(Tools3D.getCameraPickRay(app, comp, c1));
  314. Vector3D i1 = ToolsGeometry.getRayPlaneIntersection(Tools3D.getCameraPickRay(app, comp, c1), planeNormal, startP1);
  315. Vector3D i2 = ToolsGeometry.getRayPlaneIntersection(Tools3D.getCameraPickRay(app, comp, c2), planeNormal, startP1);
  316. lastMiddle = i1.getAdded(i2.getSubtracted(i1).scaleLocal(0.5f));
  317. }break;
  318. case RotateEvent.GESTURE_UPDATED:
  319. if (!oldC1.equals(c1) || !oldC2.equals(c2)){ //Because c1 and/or c2 can change if a finger with greater distance enters -> prevent jump
  320. Vector3D i1 = ToolsGeometry.getRayPlaneIntersection(Tools3D.getCameraPickRay(app, comp, c1), planeNormal, startP1);
  321. Vector3D i2 = ToolsGeometry.getRayPlaneIntersection(Tools3D.getCameraPickRay(app, comp, c2), planeNormal, startP1);
  322. lastMiddle = i1.getAdded(i2.getSubtracted(i1).scaleLocal(0.5f));
  323. oldC1 = c1;
  324. oldC2 = c2;
  325. }
  326. Vector3D i1 = ToolsGeometry.getRayPlaneIntersection(Tools3D.getCameraPickRay(app, comp, c1), planeNormal, startP1);
  327. Vector3D i2 = ToolsGeometry.getRayPlaneIntersection(Tools3D.getCameraPickRay(app, comp, c2), planeNormal, startP1);
  328. Vector3D middle = i1.getAdded(i2.getSubtracted(i1).scaleLocal(0.5f));
  329. Vector3D middleDiff = middle.getSubtracted(lastMiddle);
  330. comp.rotateZGlobal(middle, deg);
  331. comp.translateGlobal(middleDiff);
  332. lastMiddle = middle;
  333. break;
  334. case RotateEvent.GESTURE_ENDED:
  335. break;
  336. default:
  337. break;
  338. }
  339. return false;
  340. }
  341. }
  342. private class MTComplexPolyClusterable extends MTComplexPolygon implements IdragClusterable{
  343. public MTComplexPolyClusterable(PApplet app, Vertex[] vertices) {
  344. super(app, vertices);
  345. }
  346. public boolean isSelected() {
  347. return false;
  348. }
  349. public void setSelected(boolean selected) {
  350. }
  351. }
  352. private Vertex[] getTile(TileSide top, TileSide right, TileSide bottom, TileSide left){
  353. List<Vertex> list = new ArrayList<Vertex>();
  354. switch (top) {
  355. case linear:
  356. list.add(new Vertex(0,0));
  357. list.add(new Vertex(tileWidth, 0));
  358. break;
  359. case pinIn:
  360. addAll(Vertex.getDeepVertexArrayCopy(leftRightOrderHorizontalDownOut), list);
  361. break;
  362. case pinOut:
  363. addAll(Vertex.getDeepVertexArrayCopy(leftRightHorizontalUpOut), list);
  364. break;
  365. default:
  366. break;
  367. }
  368. switch (right) {
  369. case linear:
  370. // list.add(new Vertex(tileWidth,0));
  371. list.add(new Vertex(tileWidth, tileHeight));
  372. break;
  373. case pinIn:
  374. addAll(getCopyOffset(this.upDownOrderVerticalLeftOut, tileWidth, 0), list);
  375. break;
  376. case pinOut:
  377. addAll(getCopyOffset(this.upDownOrderVerticalRightOut, tileWidth, 0), list);
  378. break;
  379. default:
  380. break;
  381. }
  382. switch (bottom) {
  383. case linear:
  384. // list.add(new Vertex(tileWidth, tileHeight));
  385. list.add(new Vertex(0, tileHeight));
  386. break;
  387. case pinIn:
  388. addAll(getCopyOffset(this.rightLeftHorizontalUpOut, 0, tileHeight), list);
  389. break;
  390. case pinOut:
  391. addAll(getCopyOffset(this.rightLeftOrderHorizontalDownOut, 0, tileHeight), list);
  392. break;
  393. default:
  394. break;
  395. }
  396. switch (left) {
  397. case linear:
  398. // list.add(new Vertex(0, tileHeight));
  399. list.add(new Vertex(0, 0));
  400. break;
  401. case pinIn:
  402. addAll(Vertex.getDeepVertexArrayCopy(this.downUpOrderVerticalRightOut), list);
  403. break;
  404. case pinOut:
  405. addAll(Vertex.getDeepVertexArrayCopy(this.downUpOrderVerticalLeftOut), list);
  406. break;
  407. default:
  408. break;
  409. }
  410. return list.toArray(new Vertex[list.size()]);
  411. }
  412. private void addAll(Vertex[] vertices, List<Vertex> list){
  413. for (Vertex vertex : vertices) {
  414. list.add(vertex);
  415. }
  416. }
  417. private Vertex[] getCopyOffset(Vertex[] verts, float xOffset, float yOffset){
  418. Vertex[] copy = new Vertex[verts.length];
  419. // Vertex[] copy = Vertex.getDeepVertexArrayCopy(verts);
  420. for (int i = 0; i < copy.length; i++) {
  421. copy[i] = (Vertex) new Vertex(verts[i]).addLocal(new Vertex(xOffset, yOffset));
  422. }
  423. return copy;
  424. }
  425. private Vertex[] getInvertOrderCopyOffset(Vertex[] verts, float xOffset, float yOffset){
  426. Vertex[] copy = new Vertex[verts.length];
  427. // Vertex[] copy = Vertex.getDeepVertexArrayCopy(verts);
  428. for (int i = 0; i < copy.length; i++) {
  429. copy[i] = (Vertex) new Vertex(verts[verts.length -i -1]).addLocal(new Vertex(xOffset, yOffset));
  430. }
  431. return copy;
  432. }
  433. private Vertex[] getInvertOrderCopy(Vertex[] verts){
  434. Vertex[] copy = new Vertex[verts.length];
  435. // Vertex[] copy = Vertex.getDeepVertexArrayCopy(verts);
  436. for (int i = 0; i < verts.length; i++) {
  437. // copy[i] = copy[copy.length -i -1];
  438. copy[i] = new Vertex(verts[verts.length -i -1]);
  439. }
  440. return copy;
  441. }
  442. }