Advertisement
wblut

StepGrid

Oct 28th, 2019
545
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 18.29 KB | None | 0 0
  1. //Creates a stepped datagrid as a connected mesh using only quads and triangles
  2.  
  3. float[][] values;
  4. DataGrid grid;
  5.  
  6. void setup() {
  7.   size(1000, 1000, P3D);
  8.   smooth(8);
  9.   values=new float[12][12];
  10.   //random grid surrounded with a band of cells at value=0.0
  11.   for (int column=1; column<11; column++) {
  12.     for (int row=1; row<11; row++) {
  13.       values[column][row]=random(1.0, 5.0);
  14.     }
  15.   }
  16.   grid=new DataGrid(values, -0.2);//values as float[][], value of base
  17.   grid.saveAsSTL("demo",5, 5, 10, -30, -30, 0.2);
  18.   noCursor();
  19. }
  20.  
  21. void draw() {
  22.   background(250);
  23.   stroke(10);
  24.   translate(width/2, height/2);
  25.   lights();
  26.  
  27.   rotateX(map(mouseY, height, 0, -PI, PI));
  28.   rotateZ(map(mouseX, 0, width, -PI, PI));
  29.   //draw with parameters: size per division in X direction, size per division in X direction, scale factor for value,
  30.   //                      offset in X direction, offset in Y direction, offset in value
  31.   grid.draw(40, 40, 50, -240, -240, -100);
  32. }
  33.  
  34.  
  35.  
  36. class DataGrid {
  37.   ArrayList<GridFace> faces;
  38.   int columns, rows;
  39.   float[][] values;
  40.   float baseValue;
  41.  
  42.   DataGrid(float[][] values, float baseValue) {
  43.     assert(values.length>0);
  44.     assert(values[0].length>0);
  45.     columns=values.length;
  46.     rows=values[0].length;
  47.     this.values=values;
  48.     this.baseValue=baseValue;
  49.     createGridFaces();
  50.   }
  51.  
  52.   void createGridFaces() {
  53.     faces=new ArrayList<GridFace>();
  54.     GridPoint p1, p2, p3, p4;
  55.  
  56.     // Top faces: squares at height=value
  57.     for (int column = 0; column < columns; column++) {
  58.       for (int row = 0; row < rows; row++) {
  59.         p1 = new GridPoint(column, row, getValue(column, row, baseValue));
  60.         p2 = new GridPoint(column + 1, row, getValue(column, row, baseValue));
  61.         p3 = new GridPoint(column + 1, row + 1, getValue(column, row, baseValue));
  62.         p4 = new GridPoint(column, row + 1, getValue(column, row, baseValue));
  63.         faces.add(new GridFace(p1, p2, p3, p4));
  64.       }
  65.     }
  66.  
  67.     // Bottom faces: squares at height=base
  68.     for (int column = 0; column < columns; column++) {
  69.       for (int row = 0; row < rows; row++) {
  70.         p1 = new GridPoint(column, row, baseValue);
  71.         p2 = new GridPoint(column + 1, row, baseValue);
  72.         p3 = new GridPoint(column + 1, row + 1, baseValue);
  73.         p4 = new GridPoint(column, row + 1, baseValue);
  74.         faces.add(new GridFace(p4, p3, p2, p1));
  75.       }
  76.     }
  77.  
  78.     // If two neighboring cells have a different value, there should be a vertical wall connecting
  79.     // them. A first approach is to raw this wall as a single rectangle, ignoring the surrounding cells. This is visually
  80.     // ok and all walls are quads. However this forms a bad mesh because the walls aren't connected along their edges.
  81.     // Creating a correct mesh is more tedious and takes a lot of sketching. It looks like long code but that's because
  82.     // it has to take into account the ordering of the 4 cells sharing a vertical edge.
  83.  
  84.  
  85.     // getValue(column, row, valueIfMissing) is used to get around checking if a cell has all neighbors. This is important
  86.     // for corner cells and edge cells. If getValue is called for a cell that doesn't exist it returns the valueIfMissing.
  87.     // This is also used to create the bottom and left walls along the edges.
  88.  
  89.  
  90.     // Column Walls: create the wall between a cell and its neighbor on column+1. Column index starts at -1 to ensure
  91.     // that the left edge walls are also created.
  92.     float valueOfCell, valueOfNeighbor, v1, v2;
  93.     for (int column =-1; column <columns; column++) {
  94.       for (int row = 0; row < rows; row++) {
  95.         ArrayList<GridPoint> points = new ArrayList<GridPoint>();
  96.         valueOfCell = getValue(column, row, baseValue);
  97.         valueOfNeighbor = getValue(column + 1, row, baseValue);
  98.         //If neighbor has same value, there is no wall
  99.         if (valueOfCell != valueOfNeighbor) {
  100.           //Horizontal edge
  101.           points.add(new GridPoint(column + 1, row + 1, valueOfCell));
  102.           points.add(new GridPoint(column + 1, row, valueOfCell));
  103.           //Vertical edge, shared by up to two other cells
  104.           v1 =getValue(column, row-1, baseValue);
  105.           v2 =getValue(column+1, row-1, baseValue);
  106.           if (valueOfCell < valueOfNeighbor) {
  107.             if (valueOfCell < v1 && v1 < valueOfNeighbor && valueOfCell < v2 && v2 < valueOfNeighbor) {
  108.               if (v1 == v2) {
  109.                 points.add(new GridPoint(column + 1, row, v1));
  110.               } else if (v1 < v2) {
  111.                 points.add(new GridPoint(column+1, row, v1));
  112.                 points.add(new GridPoint(column+1, row, v2));
  113.               } else if (v2 < v1) {
  114.                 points.add(new GridPoint(column+1, row, v2));
  115.                 points.add(new GridPoint(column+1, row, v1));
  116.               }
  117.             } else if (valueOfCell < v1 && v1 < valueOfNeighbor) {
  118.               points.add(new GridPoint(column+1, row, v1));
  119.             } else if (valueOfCell < v2 && v2 < valueOfNeighbor) {
  120.               points.add(new GridPoint(column+1, row, v2));
  121.             }
  122.           } else {
  123.             if (valueOfNeighbor < v1 && v1 < valueOfCell && valueOfNeighbor < v2 && v2 < valueOfCell) {
  124.               if (v1 == v2) {
  125.                 points.add(new GridPoint(column+1, row, v1));
  126.               } else if (v2 < v1) {
  127.                 points.add(new GridPoint(column+1, row, v1));
  128.                 points.add(new GridPoint(column+1, row, v2));
  129.               } else if (v1 < v2) {
  130.                 points.add(new GridPoint(column+1, row, v2));
  131.                 points.add(new GridPoint(column+1, row, v1));
  132.               }
  133.             } else if (valueOfNeighbor < v1 && v1 < valueOfCell) {
  134.               points.add(new GridPoint(column+1, row, v1));
  135.             } else if (valueOfNeighbor < v2 && v2 < valueOfCell) {
  136.               points.add(new GridPoint(column+1, row, v2));
  137.             }
  138.           }
  139.           int numOfPointsInVerticalEdge1=points.size()-2;
  140.           //Horizontal edge
  141.           points.add(new GridPoint(column+1, row, valueOfNeighbor));
  142.           points.add(new GridPoint(column+1, row+1, valueOfNeighbor));
  143.           //Vertical edge, shared by up to two other cells
  144.           v1 = getValue(column, row+1, baseValue);
  145.           v2 = getValue(column+1, row+1, baseValue);
  146.           if (valueOfCell < valueOfNeighbor) {
  147.             if (valueOfCell < v1 && v1 < valueOfNeighbor && valueOfCell < v2 && v2 < valueOfNeighbor) {
  148.               if (v1 == v2) {
  149.                 points.add(new GridPoint(column+1, row+1, v1));
  150.               } else if (v1 < v2) {
  151.                 points.add(new GridPoint(column+1, row+1, v2));
  152.                 points.add(new GridPoint(column+1, row+1, v1));
  153.               } else if (v2 < v1) {
  154.                 points.add(new GridPoint(column+1, row+1, v1));
  155.                 points.add(new GridPoint(column+1, row+1, v2));
  156.               }
  157.             } else if (valueOfCell < v1 && v1 < valueOfNeighbor) {
  158.               points.add(new GridPoint(column+1, row+1, v1));
  159.             } else if (valueOfCell < v2 && v2 < valueOfNeighbor) {
  160.               points.add(new GridPoint(column+1, row+1, v2));
  161.             }
  162.           } else {
  163.             if (valueOfNeighbor < v1 && v1 < valueOfCell && valueOfNeighbor < v2 && v2 < valueOfCell) {
  164.               if (v1 == v2) {
  165.                 points.add(new GridPoint(column+1, row+1, v1));
  166.               } else if (v2 < v1) {
  167.                 points.add(new GridPoint(column+1, row+1, v2));
  168.                 points.add(new GridPoint(column+1, row+1, v1));
  169.               } else if (v1 < v2) {
  170.                 points.add(new GridPoint(column+1, row+1, v1));
  171.                 points.add(new GridPoint(column+1, row+1, v2));
  172.               }
  173.             } else if (valueOfNeighbor < v1 && v1 < valueOfCell) {
  174.               points.add(new GridPoint(column+1, row+1, v1));
  175.             } else if (valueOfNeighbor < v2 && v2 < valueOfCell) {
  176.               points.add(new GridPoint(column+1, row+1, v2));
  177.             }
  178.           }
  179.           int numOfPointsInVerticalEdge2=points.size()-numOfPointsInVerticalEdge1-4;
  180.           addFaces(points, numOfPointsInVerticalEdge1, numOfPointsInVerticalEdge2);
  181.         }
  182.       }
  183.     }
  184.     // Row Walls: create the wall between a cell and its neighbor on row+1.  Row index starts at -1 to ensure
  185.     // that the bottom edge walls are also created.
  186.     for (int column = 0; column < columns; column++) {
  187.       for (int row = -1; row < rows; row++) {
  188.         ArrayList<GridPoint> points = new ArrayList<GridPoint>();
  189.         valueOfCell = getValue(column, row, baseValue);
  190.         valueOfNeighbor = getValue(column, row + 1, baseValue);
  191.         //If neighbor has same value, there is no wall
  192.         if (valueOfCell != valueOfNeighbor) {
  193.           //Horizontal edge
  194.           points.add(new GridPoint(column, row+1, valueOfCell));
  195.           points.add(new GridPoint(column+1, row+1, valueOfCell));
  196.           //Vertical edge, shared by up to two other cells
  197.           v1 = getValue(column + 1, row, baseValue);
  198.           v2 = getValue(column + 1, row + 1, baseValue);
  199.           if (valueOfCell < valueOfNeighbor) {
  200.             if (valueOfCell < v1 && v1 < valueOfNeighbor && valueOfCell < v2 && v2 < valueOfNeighbor) {
  201.               if (v1 == v2) {
  202.                 points.add(new GridPoint(column+1, row+1, v1));
  203.               } else if (v1 < v2) {
  204.                 points.add(new GridPoint(column+1, row+1, v1));
  205.                 points.add(new GridPoint(column+1, row+1, v2));
  206.               } else if (v2 < v1) {
  207.                 points.add(new GridPoint(column+1, row+1, v2));
  208.                 points.add(new GridPoint(column+1, row+1, v1));
  209.               }
  210.             } else if (valueOfCell < v1 && v1 < valueOfNeighbor) {
  211.               points.add(new GridPoint(column+1, row+1, v1));
  212.             } else if (valueOfCell < v2 && v2 < valueOfNeighbor) {
  213.               points.add(new GridPoint(column+1, row+1, v2));
  214.             }
  215.           } else {
  216.             if (valueOfNeighbor < v1 && v1 < valueOfCell && valueOfNeighbor < v2 && v2 < valueOfCell) {
  217.               if (v1 == v2) {
  218.                 points.add(new GridPoint(column+1, row+1, v1));
  219.               } else if (v2 < v1) {
  220.                 points.add(new GridPoint(column+1, row+1, v1));
  221.                 points.add(new GridPoint(column+1, row+1, v2));
  222.               } else if (v1 < v2) {
  223.                 points.add(new GridPoint(column+1, row+1, v2));
  224.                 points.add(new GridPoint(column+1, row+1, v1));
  225.               }
  226.             } else if (valueOfNeighbor < v1 && v1 < valueOfCell) {
  227.               points.add(new GridPoint(column+1, row+1, v1));
  228.             } else if (valueOfNeighbor < v2 && v2 < valueOfCell) {
  229.               points.add(new GridPoint(column+1, row+1, v2));
  230.             }
  231.           }
  232.           int numOfPointsInVerticalEdge1=points.size()-2;
  233.           //Horizontal edge
  234.           points.add(new GridPoint(column+1, row+1, valueOfNeighbor));
  235.           points.add(new GridPoint(column, row+1, valueOfNeighbor));
  236.           //Vertical edge, shared by up to two other cells
  237.           v1 = getValue(column - 1, row + 1, baseValue);
  238.           v2 = getValue(column - 1, row, baseValue);
  239.           if (valueOfCell < valueOfNeighbor) {
  240.             if (valueOfCell < v1 && v1 < valueOfNeighbor && valueOfCell < v2 && v2 < valueOfNeighbor) {
  241.               if (v1 == v2) {
  242.                 points.add(new GridPoint(column, row+1, v1));
  243.               } else if (v1 < v2) {
  244.                 points.add(new GridPoint(column, row+1, v2));
  245.                 points.add(new GridPoint(column, row+1, v1));
  246.               } else if (v2 < v1) {
  247.                 points.add(new GridPoint(column, row+1, v1));
  248.                 points.add(new GridPoint(column, row+1, v2));
  249.               }
  250.             } else if (valueOfCell < v1 && v1 < valueOfNeighbor) {
  251.               points.add(new GridPoint(column, row+1, v1));
  252.             } else if (valueOfCell < v2 && v2 < valueOfNeighbor) {
  253.               points.add(new GridPoint(column, row+1, v2));
  254.             }
  255.           } else {
  256.             if (valueOfNeighbor < v1 && v1 < valueOfCell && valueOfNeighbor < v2 && v2 < valueOfCell) {
  257.               if (v1 == v2) {
  258.                 points.add(new GridPoint(column, row+1, v1));
  259.               } else if (v2 < v1) {
  260.                 points.add(new GridPoint(column, row+1, v2));
  261.                 points.add(new GridPoint(column, row+1, v1));
  262.               } else if (v1 < v2) {
  263.                 points.add(new GridPoint(column, row+1, v1));
  264.                 points.add(new GridPoint(column, row+1, v2));
  265.               }
  266.             } else if (valueOfNeighbor < v1 && v1 < valueOfCell) {
  267.               points.add(new GridPoint(column, row+1, v1));
  268.             } else if (valueOfNeighbor < v2 && v2 < valueOfCell) {
  269.               points.add(new GridPoint(column, row+1, v2));
  270.             }
  271.           }
  272.           int numOfPointsInVerticalEdge2=points.size()-numOfPointsInVerticalEdge1-4;
  273.  
  274.           addFaces(points, numOfPointsInVerticalEdge1, numOfPointsInVerticalEdge2);
  275.         }
  276.       }
  277.     }
  278.   }
  279.  
  280.  
  281.   //Each vertical edge of a wall can have up to two additional colinear vertices.
  282.   // Processing doesn't like colinear vertices. This splits the wall into triangles and quads case by case.
  283.   void addFaces(ArrayList<GridPoint> points, int numOfPointsInVerticalEdge1, int numOfPointsInVerticalEdge2) {
  284.     //all faces should be quads or triangles
  285.     if (points.size()==4) {
  286.       faces.add(new GridFace(points, 0, 1, 2, 3));
  287.     } else if (numOfPointsInVerticalEdge1==0) {
  288.       if (numOfPointsInVerticalEdge2==1) {
  289.         faces.add(new GridFace(points, 0, 1, 4));
  290.         faces.add(new GridFace(points, 1, 2, 3, 4));
  291.       } else if (numOfPointsInVerticalEdge2==2) {
  292.         faces.add(new GridFace(points, 0, 1, 5));
  293.         faces.add(new GridFace(points, 2, 3, 4));
  294.         faces.add(new GridFace(points, 4, 5, 1, 2));
  295.       }
  296.     } else if (numOfPointsInVerticalEdge1==1) {
  297.       if (numOfPointsInVerticalEdge2==0) {
  298.         faces.add(new GridFace(points, 0, 1, 2));
  299.         faces.add(new GridFace(points, 2, 3, 4, 0));
  300.       } else if (numOfPointsInVerticalEdge2==1) {
  301.         faces.add(new GridFace(points, 0, 1, 2, 5));
  302.         faces.add(new GridFace(points, 2, 3, 4, 5));
  303.       } else if (numOfPointsInVerticalEdge2==2) {
  304.         faces.add(new GridFace(points, 0, 1, 2, 6));
  305.         faces.add(new GridFace(points, 2, 3, 5, 6));
  306.         faces.add(new GridFace(points, 3, 4, 5));
  307.       }
  308.     } else if (numOfPointsInVerticalEdge1==2) {
  309.       if (numOfPointsInVerticalEdge2==0) {
  310.         faces.add(new GridFace(points, 0, 1, 2));
  311.         faces.add(new GridFace(points, 2, 3, 5, 0));
  312.         faces.add(new GridFace(points, 3, 4, 5));
  313.       } else if (numOfPointsInVerticalEdge2==1) {
  314.         faces.add(new GridFace(points, 0, 1, 2, 6));
  315.         faces.add(new GridFace(points, 2, 3, 6));
  316.         faces.add(new GridFace(points, 3, 4, 5, 6));
  317.       } else if (numOfPointsInVerticalEdge2==2) {
  318.         faces.add(new GridFace(points, 0, 1, 2, 7));
  319.         faces.add(new GridFace(points, 2, 3, 6, 7));
  320.         faces.add(new GridFace(points, 3, 4, 5, 6));
  321.       }
  322.     }
  323.   }
  324.  
  325.  
  326.  
  327.  
  328.   float getValue(int column, int row, float baseValue) {
  329.     if (column >= 0 && column < columns && row >= 0 && row < rows ) {
  330.       return values[column][row];
  331.     } else {
  332.       return baseValue;
  333.     }
  334.   }
  335.  
  336.  
  337.   void draw(float scaleX, float scaleY, float scaleValue, float offsetX, float offsetY, float offsetValue) {
  338.     for (GridFace f : faces) {
  339.       f.draw(scaleX, scaleY, scaleValue, offsetX, offsetY, offsetValue);
  340.     }
  341.   }
  342.  
  343.   void saveAsSTL(String name, float scaleX, float scaleY, float scaleValue, float offsetX, float offsetY, float offsetValue) {
  344.     PrintWriter output=createWriter(name+".stl");
  345.     output.println("solid StepGrid");
  346.     for (GridFace f : faces) {
  347.       if (f.points.length==3) {
  348.         output.println("facet normal 0 0 0");
  349.         output.println("   outer loop");
  350.         for (GridPoint p : f.points) {
  351.           output.println("   vertex "+(offsetX+scaleX*p.x)+ " "+(offsetY+scaleY*p.y)+" "+(offsetValue+scaleValue*p.value));
  352.         }
  353.         output.println("   endloop");
  354.         output.println("endfacet");
  355.       } else {
  356.         output.println("facet normal 0 0 0");
  357.         output.println("   outer loop");
  358.         GridPoint p=f.points[0];
  359.         output.println("   vertex "+(offsetX+scaleX*p.x)+ " "+(offsetY+scaleY*p.y)+" "+(offsetValue+scaleValue*p.value));
  360.         p=f.points[1];
  361.         output.println("   vertex "+(offsetX+scaleX*p.x)+ " "+(offsetY+scaleY*p.y)+" "+(offsetValue+scaleValue*p.value));
  362.         p=f.points[2];
  363.         output.println("   vertex "+(offsetX+scaleX*p.x)+ " "+(offsetY+scaleY*p.y)+" "+(offsetValue+scaleValue*p.value));
  364.         output.println("   endloop");
  365.         output.println("endfacet");
  366.         output.println("facet normal 0 0 0");
  367.         output.println("   outer loop");
  368.         output.println("   vertex "+(offsetX+scaleX*p.x)+ " "+(offsetY+scaleY*p.y)+" "+(offsetValue+scaleValue*p.value));
  369.         p=f.points[0];
  370.         output.println("   vertex "+(offsetX+scaleX*p.x)+ " "+(offsetY+scaleY*p.y)+" "+(offsetValue+scaleValue*p.value));
  371.         p=f.points[3];
  372.         output.println("   vertex "+(offsetX+scaleX*p.x)+ " "+(offsetY+scaleY*p.y)+" "+(offsetValue+scaleValue*p.value));
  373.         output.println("   endloop");
  374.         output.println("endfacet");
  375.       }
  376.     }
  377.     output.println("endsolid StepGrid");
  378.     output.flush();
  379.     output.close();
  380.   }
  381. }
  382.  
  383. class GridPoint {
  384.   float x, y, value;
  385.   GridPoint(float x, float y, float value) {
  386.     this.x=x;
  387.     this.y=y;
  388.     this.value=value;
  389.   }
  390. }
  391.  
  392. class GridFace {
  393.   GridPoint[] points;
  394.  
  395.   GridFace(ArrayList<GridPoint> pointList, int... indices) {
  396.     points=new GridPoint[indices.length];
  397.     for (int i=0; i<indices.length; i++) {
  398.       points[i]=pointList.get(indices[i]);
  399.     }
  400.   }
  401.  
  402.   GridFace(GridPoint... pointList) {
  403.     points=new GridPoint[pointList.length];
  404.     for (int i=0; i<pointList.length; i++) {
  405.       points[i]=pointList[i];
  406.     }
  407.   }
  408.  
  409.   void draw(float scaleX, float scaleY, float scaleValue, float offsetX, float offsetY, float offsetValue) {
  410.     beginShape();
  411.     for (GridPoint p : points) {
  412.       vertex(offsetX+scaleX*p.x, offsetY+scaleY*p.y, offsetValue+scaleValue*p.value);
  413.     }
  414.     endShape(CLOSE);
  415.   }
  416. }
  417.  
  418. void mousePressed() {
  419.   saveFrame("screenshot####.png");
  420. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement