Search code examples
processing

Generate a pattern and render each image on the odd rows between the images of the even rows with Processing


Looking for some help with a pattern generator build using Processing (Processing.org).

I'm trying to generate a pattern with the use of processing like this:

- - - - -
 - - - - -
- - - - - 
 - - - - -
- - - - - 

Currently I got so far that it creates a pattern like this:

- - - - - 
- - - - -  
- - - - -
- - - - -
- - - - - 

This is my code in the build.pde file:

import processing.pdf.*;
HDrawablePool pool;
HColorPool colors;

void setup(){
    size(1000,1000);
    H.init(this).background(#ffffff);
    smooth();

    colors = new HColorPool(#111111);

    pool = new HDrawablePool(1000);
    pool.autoAddToStage()
                .add(new HShape("cv_bitter01.svg"))
                .add(new HShape("cv_bitter02.svg"))
                .add(new HShape("cv_bitter03.svg"))
                .add(new HShape("cv_bitter04.svg"))
                .add(new HShape("cv_bitter05.svg"))
                .add(new HShape("cv_bitter06.svg"))
                .add(new HShape("cv_bitter07.svg"))
                .add(new HShape("cv_bitter08.svg"))
                .add(new HShape("cv_bitter09.svg"))
                .add(new HShape("cv_bitter10.svg"))    

        .layout(
            new HGridLayout()
            .startX(25)
            .startY(25)
            .spacing(40,40)
            .cols(30)
        )

        .onCreate(
            new HCallback() {
                public void run(Object obj) {
                    HShape d = (HShape) obj;
                    d
                        .enableStyle(false)
                        .strokeJoin(ROUND)
                        .strokeCap(ROUND)
                        .strokeWeight(2)
                        .stroke(#000000)
                                                .rotation( (int)random(-15,15) )
                                                .anchorAt(H.CENTER)
                    ;
                    d.randomColors(colors.fillOnly());
                                
                }
            }
        )
        .requestAll()
  ;

  saveVector();
  noLoop();
}
 
void draw() {
  H.drawStage();
}

void saveVector() {
  PGraphics tmp = null;
  tmp = beginRecord(PDF, "render.pdf");

  if (tmp == null) {
    H.drawStage();
  } else {
    H.stage().paintAll(tmp, false, 1); // PGraphics, uses3D, alpha
  }

  endRecord();
}

Solution

  • HGridLayout is a rectangular grid.

    For the pattern you're after use HHexLayout instead.

    Have a look at the following HHexLayout examples:

    Update You can get away with a Hex layout, however, after having a closer look, there will be elements drawn that are offscreen.

    The pattern is also known as a diagrid and you could generate it in multiple ways. One basic method is keep offset every other row on the x axis. You can check if a row is even or odd using the remainder of the integer division operation using the modulo(%) operator:

    for(int i = 0; i < 10; i++){
      if(i % 2 == 0){
        println(i, "is even)");
      }else{
        println(i, "is odd)");
      }
    }
    

    If you look at the HGridLayout source code you'll notice how the x,y positions are calculated based on the row, column index.

    You can make a copy of this class, name it say HDiagrid and on top of the same grid behaviour, add an extra variable to keep track of the extra offset on the x axis to be applied ever other row. In your text annotion the offset is applied to odd rows, so use row % 2 == 1 to check if the current position to calculate is on an odd row, therefore the diagonal grid x offset should be applied.

    Here's a basic example, remixing Hype's HGridLayout_001:

    import hype.*;
    import hype.extended.layout.HGridLayout;
    
    HDrawablePool pool;
    
    void setup() {
        size(640,640);
        H.init(this).background(#242424);
    
        pool = new HDrawablePool(192);
        pool.autoAddToStage()
            .add(new HRect(25).rounding(4))
    
            .layout(
                new HDiagridLayout()
          //new HGridLayout()
                .startX(28)
                .startY(21)
                .spacing(26 * 3, 26)
                .cols(8)
            )
            
            .onCreate(
                 new HCallback() {
                    public void run(Object obj) {
                        HDrawable d = (HDrawable) obj;
                        d.noStroke().fill(#ECECEC).anchorAt(H.CENTER);
                    }
                }
            )
            .requestAll()
        ;
    
        H.drawStage();
        noLoop();
    }
    
    void draw() {
    
    }
    
    import hype.HDrawable;
    import hype.interfaces.HLayout;
    import processing.core.PVector;
    
    // a modified copy of https://github.com/hype/HYPE_Processing/blob/master/src/main/java/hype/extended/layout/HGridLayout.java
    
    public class HDiagridLayout implements HLayout {
      private int currentIndex, numCols, numRows;
      private float startX, startY, startZ, xSpace, ySpace, zSpace;
      // extra property to for diagonal grid horizontal offset
      private float diagridOffset;
    
      public HDiagridLayout() {
        xSpace = ySpace = zSpace = numCols = 16;
        // update horizontal offset
        diagridOffset = xSpace * 0.5;
        numRows = 0;
      }
    
      public HDiagridLayout(int numOfColumns) {
        this();
        numCols = numOfColumns;
      }
    
      public HDiagridLayout(int numOfColumns, int numOfRows) {
        this();
        numCols = numOfColumns;
        numRows = numOfRows;
      }
    
      public HDiagridLayout currentIndex(int i) {
        currentIndex = i;
        return this;
      }
    
      public int currentIndex() {
        return currentIndex;
      }
    
      public HDiagridLayout resetIndex() {
        currentIndex = 0;
        return this;
      }
    
      public HDiagridLayout cols(int numOfColumns) {
        numCols = numOfColumns;
        return this;
      }
    
      public int cols() {
        return numCols;
      }
    
      public HDiagridLayout rows(int numOfRows) {
        numRows = numOfRows;
        return this;
      }
    
      public int rows() {
        return numRows;
      }
    
      public PVector startLoc() {
        return new PVector(startX, startY, startZ);
      }
    
      public HDiagridLayout startLoc(float x, float y) {
        startX = x;
        startY = y;
        startZ = 0;
        return this;
      }
    
      public HDiagridLayout startLoc(float x, float y, float z) {
        startX = x;
        startY = y;
        startZ = z;
        return this;
      }
    
      public float startX() {
        return startX;
      }
    
      public HDiagridLayout startX(float x) {
        startX = x;
        return this;
      }
    
      public float startY() {
        return startY;
      }
    
      public HDiagridLayout startY(float y) {
        startY = y;
        return this;
      }
    
      public float startZ() {
        return startZ;
      }
    
      public HDiagridLayout startZ(float z) {
        startZ = z;
        return this;
      }
    
      public PVector spacing() {
        return new PVector(xSpace, ySpace, zSpace);
      }
    
      public HDiagridLayout spacing(float xSpacing, float ySpacing) {
        xSpace = xSpacing;
        ySpace = ySpacing;
        // update horizontal offset
        diagridOffset = xSpace * 0.5;
        return this;
      }
    
      public HDiagridLayout spacing(float xSpacing, float ySpacing, float zSpacing) {
        xSpace = xSpacing;
        ySpace = ySpacing;
        zSpace = zSpacing;
        // update horizontal offset
        diagridOffset = xSpace * 0.5;
        return this;
      }
    
      public float spacingX() {
        return xSpace;
      }
    
      public HDiagridLayout spacingX(float xSpacing) {
        xSpace = xSpacing;
        // update horizontal offset
        diagridOffset = xSpace * 0.5;
        return this;
      }
    
      public float spacingY() {
        return ySpace;
      }
    
      public HDiagridLayout spacingY(float ySpacing) {
        ySpace = ySpacing;
        return this;
      }
    
      public float spacingZ() {
        return zSpace;
      }
    
      public HDiagridLayout spacingZ(float zSpacing) {
        zSpace = zSpacing;
        return this;
      }
    
      @Override
      public PVector getNextPoint() {
    
        int layer = 0;
        int row = 0;
        int col = currentIndex % numCols;
    
        if (numRows > 0) {
          layer = (int) Math.floor( currentIndex / (numCols * numRows) );
          row = (int) Math.floor(currentIndex / numCols) - (layer * numRows);
        } else {
          row = (int) Math.floor(currentIndex / numCols);
        }
    
        ++currentIndex;
        // every other row the x value will be offset by an amount
        // on even rows it's 9
        float xOffset = 0;
        // if the row is odd (remainder of integer division by 2 is 1) (see % operator reference)
        if(row %  2 == 1){
          // apply the diagonal grid offset
          xOffset = diagridOffset;
        }
        // add xOffset (be it 0 on even rows or not
        if (numRows > 0) {
          return new PVector(col* xSpace + startX + xOffset, row* ySpace + startY, layer* zSpace + startZ);
        } else {
          return new PVector(col* xSpace + startX + xOffset, row* ySpace + startY);
        }
      }
    
      @Override
      public void applyTo(HDrawable target) {
        target.loc(getNextPoint());
      }
    }
    

    Which produces this:

    diagrid example

    Notice in the example above the diagridOffset is half of spacingX. This is just an example: it can be another proportion or it can even be independent of spacingX. Additionally you can add getter and setter methods (similar to public float spacingX() and public HDiagridLayout spacingX(float xSpacing) for example).

    Have fun!