Tools:

Type Morpher Flexible Motion Grid Rhythmic Glyphs

Flexible Motion Grid

Flexible motion grid is a generative design system that reinvents one of the most mundane computer actions since its invention: dragging rectangles to select items. The tool explores how such repetitive shapes can be used to create information and patterns in motion. The tool uses a symmetrical grid where a quadrilateral of any dimension can be drawn with different attributes and text inputs.

Flexible Motion Grid: Transformation

The transformation o Flexible Motion Grid is a moving object along a quadrilateral path, which is constantly recalculated according to its dimensions. Its dimensions are moved by a dragging effect with the mouse. The motion path calculation for the moving object starts when the mouse is clicked on the canvas and ends when it is released

Relevating coding functions for a grid transformation

 
            var drawThings;

var initX, initY;
var posX, posY;
var globalSpeed = 3;
var baseSpeed = globalSpeed;
var Yspeed = globalSpeed;
var Xspeed = globalSpeed;

var rectWidth, rectHeight;

var minX, minY;
var maxX, maxY;

function setup() {
  createCanvas(600, 600);
  noFill();
  strokeWeight(4);
  stroke(255);
}

function draw() {
  background(0);
  if (drawThings) {
    init();
  }
  
  push()
  noStroke()
  fill(255,0,0)
  ellipse(mouseX, mouseY, 20)
  pop()
}

function mousePressed() {
  drawThings = !drawThings;

  initX = mouseX;
  initY = mouseY;

  posX = initX;
  posY = initY;
}

function mouseReleased() {
  initX = mouseX;
  initY = mouseY;
}

function init() {
  // var distVar = dist(initX,initY, mouseX,mouseY)
  rectWidth = mouseX - initX;
  rectHeight = mouseY - initY;

  rect(initX, initY, rectWidth, rectHeight);

  minX = initX;
  minY = initY;

  maxY = rectHeight + initY;
  maxX = rectWidth + initX;

  if (rectWidth >= 50) {
    if (rectHeight >= 50) {
      // first speed horizontal
      Yspeed = 0;
      posX = posX + baseSpeed;
      posY = posY + Yspeed;

      // when value is bigger or same as maxX, stop moving X and move Y.
      if (posX >= maxX) {
        Yspeed = globalSpeed;
        posX = maxX;
        posY = posY + Yspeed;
        baseSpeed = 50;
      }

      // when Y is bigger or same as Y, stop the base Speed,
      if (posY >= maxY) {
        baseSpeed = 0;
        Xspeed = -globalSpeed;

        posX = posX + Xspeed;
        posY = maxY;
      }

      //
      if (Xspeed == -globalSpeed && posY < maxY) {
        Yspeed = 50;
        posY = maxY;
      }

      //
      if (posX <= minX) {
        baseSpeed = 0;
        Xspeed = 0;
        posY = posY - globalSpeed;
      }

      //
      if (posY <= minY) {
        Xspeed = globalSpeed;
        baseSpeed = 0;
        posX = posX + Xspeed;
      }
    }
  }

  if (rectWidth < -1) {
    if (rectHeight < -1) {
      Yspeed = 0;
      posX = posX - baseSpeed;
      posY = posY + Yspeed;

      // when value is bigger or same as maxX, stop moving X and move Y.
      if (posX <= maxX) {
        Yspeed = -globalSpeed;
        posX = maxX;
        posY = posY + Yspeed;
        baseSpeed = 50;
      }

      // when Y is bigger or same as Y, stop the base Speed,
      if (posY <= maxY) {
        baseSpeed = 0;
        Xspeed = +globalSpeed;

        posX = posX + Xspeed;
        posY = maxY;
      }

      if (Xspeed == globalSpeed && posY < maxY) {
        Yspeed = 50;
        posY = maxY;
      }

      if (posX >= minX) {
        baseSpeed = 0;
        Xspeed = 0;
        posY = posY + globalSpeed;
      }
    }
  }

  ellipse(posX, posY, 50);
}

                        
                          
                        
Open the sketch in the p5 editor ↗

Flexible Motion Grid: Parameters

The parameters are independently applied to each new quadrilateral, instead of being applied to all of them. Thus, for each new quadrilateral, new parametric values can be chosen. The parameters are velocity, grid scale, width spacing, height spacing, shear factor, type/shape, rainbow/solid color, and pattern size. Below are examples of the parameters type/shape and grid scale. In the example below is a sketch that shows different grids with different parametric values for the size. The code displays a small fraction of the whole program.

Relevating coding functions for a grid transformation

 
          
          var globalScale = 25; 
          
          function setup() {
            sliderScale = createSlider(1, 4, 1, 1); 
            sliderScale.addClass("sliders");
            sliderScale.parent("sliderGridScale");
          }
          
          function draw() {
            grid = globalScale; 
            var gridOffset = grid / 1;
          
            if (chk1.checked) {
              displayGrid(); 
            }
          }
          
          function displayGrid() {
            push();
            stroke(100);
            strokeWeight(1);
            while (l < width || l < height) {
              line(0, l, width, l); 
              line(l, 0, l, height); 
              l += grid; 
            }
            pop();
          }
          
          function newBlock() {
            let scale = sliderScale.value(); 
          }
          

                        
Open the sketch in the p5 editor ↗

Flexible Motion Grid: Iterations

Below are shown several iterations of Flexible Motion Grid. Each iteration was a step towards a the fully functional generative system. Below are recorded iterations. The code displays an example of a single interation, where the code was built on top of the core transformation, creating a more complete system overtime.

 
          class MovingShape {
            constructor(globalSpeed) {
              this.globalSpeed = globalSpeed;
              this.baseSpeed = this.globalSpeed;
              this.Yspeed = this.globalSpeed;
              this.Xspeed = this.globalSpeed;
              this.rectWidth = 0;
              this.rectHeight = 0;
              this.margin = 5;
              this.patternDensity = 10;
              this.lineSpeed = 1;
              this.lineY;
          
              this.triggerOffset = 25;
              this.isDragging = false;
            }
          
            initShape() {
              if (this.isDragging) {
                this.init();
              }
            }
          
            shapePressed() {
              this.isDragging = true;
              this.initX = mouseX;
              this.initY = mouseY;
          
              this.posX = this.initX;
              this.posY = this.initY;
          
              this.lineY = this.initY;
            }
          
            shapeReleased() {
              this.isDragging = false;
              this.globalSpeed = this.globalSpeed;
              this.baseSpeed = this.globalSpeed;
            }
          
            init() {
              this.rectWidth = mouseX - this.initX;
              this.rectHeight = mouseY - this.initY;
          
              this.minX = this.initX;
              this.minY = this.initY;
          
              this.maxY = this.rectHeight + this.initY;
              this.maxX = this.rectWidth + this.initX;
            }
          
            moveInside() {
              if (
                this.rectWidth >= this.triggerOffset &&
                this.rectHeight >= this.triggerOffset
              ) {
                this.lineY = this.lineY + this.lineSpeed;
          
                if (this.lineY > this.maxY) {
                  this.lineY = this.minY;
                }
              }
            }
          
            moveShape() {
              // if the dragged values are higher than the trigger value in the constructor, activate the movement
              if (
                this.rectWidth >= this.triggerOffset &&
                this.rectHeight >= this.triggerOffset
              ) {
                this.Yspeed = 0;
                this.posX += this.baseSpeed;
                this.posY += this.Yspeed;
          
                // if the X value is greater than maxX, stop X value and make Y value move
                if (this.posX >= this.maxX) {
                  this.Yspeed = this.globalSpeed;
                  this.posX = this.maxX;
                  this.posY += this.Yspeed;
                  // very imporant! if the rect size gets bigger, the moving object readjusts with the baseSpeed
                  this.baseSpeed = 100;
                }
          
                if (this.posY >= this.maxY) {
                  this.baseSpeed = 0;
                  this.Xspeed = -this.globalSpeed;
                  this.posX += this.Xspeed;
                  this.posY = this.maxY;
                }
          
                if (this.Xspeed === -this.globalSpeed && this.posY < this.maxY) {
                  // very imporant! if the rect size gets bigger, the moving object readjusts with the baseSpeed
                  this.Yspeed = 100;
                  this.posY = this.maxY;
                }
          
                if (this.posX <= this.minX) {
                  this.baseSpeed = 0;
                  this.Xspeed = 0;
                  this.posY -= this.globalSpeed;
                }
          
                if (this.posY <= this.minY) {
                  this.Xspeed = this.globalSpeed;
                  this.baseSpeed = 0;
                  this.posX += this.Xspeed;
                }
              }
            }
            displayShape() {
              push();
              fill(255, 0, 0);
              noStroke();
              ellipse(this.posX, this.posY, 10);
              pop();
            }
          
            displayInside() {
              stroke(255);
              strokeWeight(2);
              //line(this.minX + 25, this.lineY, this.maxX - 25, this.lineY);
          
              noStroke();
              textSize(50);
              fill(255);
              // textAlign(CENTER)
          
              // text("The quick brown fox jumps over the lazy dog",  this.minX + (this.rectWidth / 2), this.minY + (this.rectHeight / 2)+25)
          
              textAlign(LEFT);
          
              text(
                "The quick brown fox jumps over the lazy dog",
                this.minX + 25,
                this.minY + 25,
                this.rectWidth - 25,
                this.rectHeight - 25
              );
            }
          
            displayRectangle() {
              noFill();
              strokeWeight(2);
              stroke(255);
              rect(this.initX, this.initY, this.rectWidth, this.rectHeight);
            }
          
            drawPattern() {
              push();
              rectMode(CENTER);
              noStroke();
              // loop of objects top side of rectangle
              for (var i = this.minX; i <= this.maxX; i += this.patternDensity) {
                rect(i, this.minY, this.margin * 2);
              }
          
              // loop of objects right side of rectangle
              for (var i = this.minY; i <= this.maxY; i += this.patternDensity) {
                rect(this.maxX, i, this.margin * 2);
              }
          
              // loop of objects bottom side of rectangle
              for (var i = this.maxX; i >= this.minX; i -= this.patternDensity) {
                rect(i, this.maxY, this.margin * 2);
              }
          
              // loop of objects left side of rectangle
              for (var i = this.maxY; i >= this.minY; i -= this.patternDensity) {
                rect(this.minX, i, this.margin * 2);
              }
          
              //this rect covers the uncompleted last rectangle of the left side loop of objects
              rect(this.minX, this.minY, this.margin * 2);
          
              fill(0);
          
              beginShape();
              vertex(this.minX + this.margin, this.minY + this.margin);
              vertex(this.maxX - this.margin, this.minY + this.margin);
              vertex(this.maxX - this.margin, this.maxY - this.margin);
              vertex(this.minX + this.margin, this.maxY - this.margin);
              endShape(CLOSE);
              pop();
            }
          }
          
          class Block {
            constructor(globalSpeed) {
              this.movingRect = new MovingShape();
          
              this.movingShape = new MovingShape(globalSpeed);
            }
          
            update() {
              this.movingRect.initShape();
              this.movingRect.drawPattern();
              //this.movingRect.displayInside();
              //this.movingShape.moveInside();
          
              this.movingShape.initShape();
              this.movingShape.displayShape();
              this.movingShape.moveShape();
            }
          }
          
          let blocks = [];
          let currentBlock;
          
          function setup() {
            createCanvas(600, 600);
          
            newBlock();
          }
          
          function draw() {
            background(100);
          
            for (let i = 0; i < blocks.length; i++) {
              blocks[i].update();
            }
          }
          
          function newBlock() {
            currentBlock = new Block(3);
            blocks.push(currentBlock);
          }
          
          function mousePressed() {
            currentBlock.movingRect.shapePressed();
            currentBlock.movingShape.shapePressed();
          }
          
          function mouseReleased() {
            currentBlock.movingRect.shapeReleased();
            currentBlock.movingShape.shapeReleased();
          
            newBlock();
          }
          
                        

Flexible Motion Grid: Instances

Below are three diferente instances of Flexible Motion Grid, each with different parametric values and inputs.

NEXT:

Use this tool ↗

Rhythmic Glyphs

Watch use cases