Huichol Pattern Generator
Custom Software for the creation of animated patterns based on MesoAmerican Huichol Art
Images / Code Snippets

The Huichol Pattern Generator is software for the generation of colorfull patterns that animate over a grid. It is based on the kind of structure present in the art of the Huichol, a Mexican MesoAmerican culture.

Their art usually depicts animals and symbols from their mythology, constructed with very colorfull geometric patterns, highly saturated palettes, and composed with beads arranged over an hexagonal grid that completely covers the statue or artwork.

The software is originally coded to generate patterns that will be later composed for a projection of a fullDome experience at the Buenos Aires Planetarium, as part of the Understanding Visual Music International Conference 2016.

Along with Mario Guzmán (Mario's webSite), we are putting together a piece of animated motion graphics that takes the audience inside a Mexican Temazcal, to embark on a visually intense journey towards the constellation of The Pleiades.

The software is in beta v0.9, with some minor bugs but ready for production use.
Code repository (“dev” branch): https://github.com/pleekMan/HuicholArtGenerator/tree/dev


With the Huichol Pattern Generator, you can:

  • Draw Spawn points, move them around in the grid, change their shooting vectors or remove them from the canvas. All of this can also be done while on playback and rendering..!!
  • Play, pause and rewind pattern animations.
  • Set how many times a figure will loop.
  • Draw only lines or interpolate across the points.

  • Move and scale the canvas to work more confortably (It is now supporting a canvas size of 4096 x 4096 pixels, with lots of points..!!).
  • Hide/Show viewport gizmos such as the grid or the figure’s spawn points.
  • Load an image on the background, scale it, change it’s opacity & lock it to the viewport scale. An image can serve as reference to construct certain kind of figure, and also to pick specific colors from it.

  • Create new color palettes by picking from the viewport or from a color spectrum.
  • Assign palettes to figures, delete them or modify them.
  • Color Palettes are saved, so the next time you the application is opened, they are all there happily waiting to by assigned..!!

  • Rendering: Choose a name and render the animation as a PNG sequence.
  • Specify a Region of Interest, you don’t need to render out the entire canvas..!!

 

Coded in Processing

// Images

// The Sun

// The Chamán

// The Software's Editor Options and Color Palette Sidebar

// Code Snippets

Some Processing code snippets used in this software, that might come in handy for other stuff:

// View to Canvas Transform:
If you are dealing with selecting elements on a canvas that can be moved around and scaled, you might need to transform the coordinates of your mouse clicking in your view/window to the coordinates of the canvas/layer that your elements are in, and viceversa.

    
PVector viewToCanvasTransform(PVector viewCoords, PVector canvasTranslation, float canvasScale) {
  PVector invertedTranslation = PVector.mult(canvasTranslation, -1);
  PVector newCoords = PVector.add(viewCoords, invertedTranslation);
  return newCoords.mult(1.0f / canvasScale);
}


PVector canvasToViewTransform(PVector canvasCoords, PVector canvasTranslation, float canvasScale) {
  PVector scaledCoord = PVector.mult(canvasCoords, canvasScale);
  return PVector.add(scaledCoord, canvasTranslation);
}
      

// Neighbour detection over hexagonal grid:
Useful for things like an hexagonal Game of Life. This code considers that the grid is generated by rows: one complete horizontal line, then the next one from the left. And that the second row (and all other odd rows) is the one that is shifted to the right to form the hexagonal grid.
It returns an array of ints with the index values of the points neighbouring the center one selected.

   
int[] getNeighboursIndex(int index, int gridWidth, int pointsCount) {

  int[] neighbours;

  // CHECK: 1)CORNER POINTS -> 2)BORDERS -> 3)INSIDE OF GRID

  if (index == 0) {
    // FIRST POINT
    neighbours = new int[2];
    neighbours[0] = index + 1;
    neighbours[1] = index + gridWidth;
    return neighbours;
    
  } else if (index == (gridWidth - 1)) {
    // TOP RIGHT POINT
    neighbours = new int[3];
    neighbours[0] = index + gridWidth;
    neighbours[1] = index + gridWidth - 1;
    neighbours[2] = index - 1;
    return neighbours;
    
  } else if (index == pointsCount - 1) {
    // BOTTOM RIGHT (LAST) POINT
    if (isEvenRow(index - (gridWidth - 1), gridWidth)) {
      neighbours = new int[3];
      neighbours[0] = index - 1;
      neighbours[1] = index - gridWidth - 1;
      neighbours[2] = index - gridWidth;
      return neighbours;
    } else {
      neighbours = new int[2];
      neighbours[0] = index - 1;
      neighbours[1] = index - gridWidth;
      return neighbours;
      
    }
  } else if (index == (pointsCount - gridWidth)) {
    // BOTTOM LEFT POINT
    if (isEvenRow(index, gridWidth)) {
      neighbours = new int[2];
      neighbours[0] = index - gridWidth;
      neighbours[1] = index + 1;
      return neighbours;
      
    } else {
      neighbours = new int[3];
      neighbours[0] = index - gridWidth;
      neighbours[1] = index - gridWidth + 1;
      neighbours[2] = index + 1;
      return neighbours;
      
    }
  } else if (index < gridWidth) {
    // TOP BORDER
    neighbours = new int[4];
    neighbours[0] = index + 1;
    neighbours[1] = index + gridWidth;
    neighbours[2] = index + gridWidth - 1;
    neighbours[3] = index - 1;
    return neighbours;
    
  } else if ((index - (gridWidth - 1)) % gridWidth == 0) {
    // RIGHT BORDER
    if (isEvenRow(index - (gridWidth - 1), gridWidth)) {
      neighbours = new int[5];
      neighbours[0] = index + gridWidth;
      neighbours[1] = index + gridWidth - 1;
      neighbours[2] = index - 1;
      neighbours[3] = index - gridWidth - 1;
      neighbours[4] = index - gridWidth;
      return neighbours;
      
    } else {
      neighbours = new int[3];
      neighbours[0] = index + gridWidth;
      neighbours[1] = index - 1;
      neighbours[2] = index - gridWidth;
      return neighbours;
      
    }
  } else if (index > (pointsCount - gridWidth)) {
    // BOTTOM BORDER
    if (isEvenRow(index, gridWidth)) {
      neighbours = new int[4];
      neighbours[0] = index - 1;
      neighbours[1] = index - gridWidth - 1;
      neighbours[2] = index - gridWidth;
      neighbours[3] = index + 1;
      return neighbours;
      
    } else {
      neighbours = new int[4];
      neighbours[0] = index - 1;
      neighbours[1] = index - gridWidth;
      neighbours[2] = index - gridWidth + 1;
      neighbours[3] = index + 1;
      return neighbours;
      
    }
  } else if (index % gridWidth == 0) {
    // LEFT BORDER
    if (isEvenRow(index, gridWidth)) {
      neighbours = new int[3];
      neighbours[0] = index - gridWidth;
      neighbours[1] = index + 1;
      neighbours[2] = index + gridWidth;
      return neighbours;
      
    } else {
      neighbours = new int[5];
      neighbours[0] = index - gridWidth;
      neighbours[1] = index - gridWidth + 1;
      neighbours[2] = index + 1;
      neighbours[3] = index + gridWidth + 1;
      neighbours[4] = index + gridWidth;
      return neighbours;
      
    }
  } else {
    // THE POINT IS INSIDE THE GRID
    if (isEvenRow(index - (index % gridWidth), gridWidth)) {
      neighbours = new int[6];
      neighbours[0] = index + 1;
      neighbours[1] = index + gridWidth;
      neighbours[2] = index + gridWidth - 1;
      neighbours[3] = index - 1;
      neighbours[4] = index - gridWidth - 1;
      neighbours[5] = index - gridWidth;
      return neighbours;
      
    } else {
      neighbours = new int[6];
      neighbours[0] = index + 1;
      neighbours[1] = index + gridWidth + 1;
      neighbours[2] = index + gridWidth;
      neighbours[3] = index - 1;
      neighbours[4] = index - gridWidth;
      neighbours[5] = index - gridWidth + 1;
      return neighbours;
      
    }
  }
}


boolean isEvenRow(int i, int gridWidth) {
  // CALCULATES IF ROW IS EVEN/ODD BASED ON INDEX OF FIRST POINT IN ROW
  return (i / gridWidth) % 2 == 0 ? true : false;
}