How to make a Circle to Square pattern, or shape to another shape pattern

jsrjsr
edited June 2016 in How To...

Hi there,

Im wanting to make a pattern that would have a simple for loop repeating pattern. Im thinking of something that starts on one quadrant and finishes on another quadrant. For sake of discussion a triangle that morphs in to a square?

I saw this https://processing.org/examples/morph.html which helps me think about how to morph a shape, but not sure if the same rule would apply for wanting to make a pattern.

Generative Design has a similar sketch http://www.generative-gestaltung.de/P_2_1_2_03 but I do not need sketch to move, a still image is totally fine.

Any help is much appreciated!

Answers

  • Based on where a shape is drawn, make it more like a triangle or more like a square.

    You know where the shape is going to be drawn, because you know where you're drawing it.

    You might consider using the distance from (0,0) to where the shape is being drawn as the amount of how "like a square" your shape is. At (width,height), this would be 100% square (and thus 0% triangle). At (0,0), 100% triangle and 0% square.

    Here's some example code. Understand it first:

    void setup(){
      size(200,200);
      noFill();
      strokeWeight(2);
      smooth();
    }
    void draw(){
      background(0);
      morph_shape(map(dist(0,0,mouseX,mouseY),0,dist(0,0,width,height),0,1));
    }
    
    void morph_shape(float amt){
      stroke(lerpColor(color(255,0,0),color(0,0,255),amt));
      rect(mouseX-10,mouseY-10,20+10*amt,20+10*amt,20*amt);
    }
    
  • edited June 2016

    Nice one @TfGuy44 & nice idea @jsr

    Been playing with the original code but don't know PVector class well enough.

    I'd meant to spend this evening sorting out a drum sequencer in arduino, but really liking Islamic art made this too interesting to miss.

    Anyway, this is a simple tiling of code above:

    `int tileX, tileY;
    int tileDistance = 30;
    float tileNumber = 0;
    
    void setup() {
      size(640, 360);
      noFill();
      stroke(255);
    }
    
    void draw() {
      background(51);
      noLoop();
      for (tileY = 0; tileY < height; tileY += tileDistance) {
        for (tileX = 0; tileX < width; tileX += tileDistance) {
          tileNumber ++;
          strokeWeight(2);
          morph_shape(tileNumber);
        }
      }
    }
    
    void morph_shape(float amt) {
      rect(tileX, tileY, tileDistance, tileDistance, amt/5);
    }
    `
    

    But being able to morph between different shapes would be amazing - PVector looks to be the way to go, but how to deal with polygons of different numbers of sides - how to morph between a square and a triangle?

  • At least make it tile my shapes nice.

    int tileX, tileY;
    int tileDistance = 30;
    float tileNumber = 0;
    
    void setup() {
      size(640, 360);
      noFill();
      stroke(255);
      strokeWeight(2);
    }
    
    void draw() {
      background(0);
      noLoop();
      for (tileY = 10; tileY < height; tileY += tileDistance+5) {
        for (tileX = 10; tileX < width; tileX += tileDistance+5) {
          morph_shape(map(dist(0,0,tileX,tileY),0,dist(0,0,width,height),0,1));
        }
      }
    }
    
    void morph_shape(float amt) {
      stroke(lerpColor(color(255,0,0),color(0,0,255),amt));
      rect(tileX, tileY,20+10*amt,20+10*amt,20*amt);
    }
    
  • Here's one way:

    int tileX, tileY;
    int tileDistance = 30;
    float tileNumber = 0;
    
    void setup() {
      size(640, 360);
      noFill();
      stroke(255);
      strokeWeight(2);
    }
    
    void draw() {
      background(0);
      noLoop();
      for (tileY = 10; tileY < height; tileY += tileDistance+5) {
        for (tileX = 10; tileX < width; tileX += tileDistance+5) {
          morph_shape(map(dist(0,0,tileX,tileY),0,dist(0,0,width,height),0,1));
        }
      }
    }
    
    void morph_shape(float amt) {
      stroke(lerpColor(color(255,0,0),color(0,0,255),amt));
      //rect(tileX, tileY,20+10*amt,20+10*amt,20*amt);
      pushMatrix();
      translate(tileX,tileY);
      beginShape();
        vertex(0,20*amt);
        vertex(20-10*amt,0);
        vertex(20,20);
        vertex(0,20);
      endShape(CLOSE);
      popMatrix();
    }
    
  • Or, if you like:

    int tileX, tileY;
    int tileDistance = 30;
    float tileNumber = 0;
    
    void setup() {
      size(640, 360);
      noFill();
      stroke(255);
      strokeWeight(2);
    }
    
    void draw() {
      background(0);
      noLoop();
      for (tileY = 10; tileY < height; tileY += tileDistance+5) {
        for (tileX = 10; tileX < width; tileX += tileDistance+5) {
          morph_shape(map(dist(0,0,tileX,tileY),0,dist(0,0,width,height),0,1));
        }
      }
    }
    
    void morph_shape(float amt) {
      stroke(lerpColor(color(255,0,0),color(0,0,255),amt));
      //rect(tileX, tileY,20+10*amt,20+10*amt,20*amt);
      pushMatrix();
      translate(tileX,tileY);
      beginShape();
        vertex(0,20*amt);
        vertex(10,0);
        vertex(20,20*amt);    
        vertex(20,20);
        vertex(0,20);
      endShape(CLOSE);
      popMatrix();
    }
    
  • edited June 2016

    That's really nice. Hadn't understood the morph_shape amt but good way of spreading change through the grid. I like the middle one most. Doesn't scale with change of tileDistance unless you use:

      beginShape();
        vertex(0,(tileDistance * 0.66)*amt);
        vertex((tileDistance * 0.66) - (tileDistance * 0.33 * amt),0);
        vertex((tileDistance * 0.66) , (tileDistance * 0.66));
        vertex(0,(tileDistance * 0.66));
      endShape(CLOSE);
    
  • edited June 2016

    Is there a way of getting it between to morph between polygons of n sides?

    edit - looks like this is how to draw polygon of n sides, next step to lerp betweeen vertices. not tested yet, so bound to have an error or two.

        int n = 3;
        float radius = 30;
        float theta = TWO_PI / n;
    
        beginShape();     
        for (int i = 0; i < n; i ++) {
            float x = radius * cos(theta * i);
            float y = radius * sin(theta * i);
            vertex (x , y);
        }
        endShape(CLOSE);
    
  • edited June 2016

    Not perfect, but this interpolates between 2 polygons of (different) n sides. Quite a complicated way of setting out spokes, then limiting the magnitude for those ones not needed, but used in interpolating the next shape.

        // this interpolates from a shape of random sides to a shape of a different, random number of sides
    
        int tileX, tileY;
        int tileDistance = 60;
        float tileNumber = 0;
    
        PolygonN shapeone, shapetwo;
        int maxSides = 6;
        float amt = 0;
    
        int n, maxN, n2; 
    
        PVector origin;
        int radius = 20;
        int originX, originY;
    
        void setup() {
          size (800, 600);
          background(0);
          stroke(255);
          strokeWeight(2);
    
          originX = width / 2;
          originY = height / 2;
    
          n = (int)random(maxSides) + 3;
          n2 = (int)random(maxSides) + 3;
          if (n == n2) {
            n2 = n + 1;
          }
          maxN = n * n2 * 20;
    
          shapeone = new PolygonN(n, maxN, radius);
          shapetwo = new PolygonN(n2, maxN, radius);
    
          shapeone.setupPolygon();
          shapetwo.setupPolygon();
        } // end setup
    
        void draw() {
    
          for (tileY = tileDistance / 2; tileY < height; tileY += tileDistance) {
            for (tileX = tileDistance / 2; tileX < width; tileX += tileDistance) {
              pushMatrix();
              translate(tileX, tileY);
              amt = map(dist(tileDistance, tileDistance, tileX, tileY), 0, dist(tileDistance, tileDistance, width - tileDistance, height - tileDistance), 0, 1);
                stroke(lerpColor(color(255, 0, 0), color(0, 0, 255), amt));
    
              PVector[] morph = new PVector[maxN];
    
              noFill();
              beginShape();
              for (int i = 0; i < maxN; i ++) {
                morph[i] = PVector.lerp(shapeone.v[i], shapetwo.v[i], amt);
                vertex(morph[i].x, morph[i].y);
              }
              endShape(CLOSE);
              popMatrix();
            }
          }
          noLoop();
        } // end draw
    
    
        class PolygonN {
          int sides;
          int spokes;
          int radius;
          PVector[] v; // all spokes from origin
          PVector[] spoke; // spokes to each corner ie used spokes
          PVector origin = new PVector(0, 0);
    
          PolygonN(int n, int m, int r) {
            sides = n;
            spokes = m;
            radius = r;
          }
    
          void setupPolygon() {
    
            float theta = TWO_PI / sides; // angle between sides
            float thetaSpokes = TWO_PI / spokes; // angle between spokes
            int currentSpoke = 1;
            PVector overlap; // working vector to tell if spokes too long
    
            spoke = new PVector[sides+1];
            v = new PVector[spokes];
    
            for (int i = 0; i < sides; i ++) { // set up n used spokes
              spoke[i] = PVector.fromAngle(theta * i); 
              spoke[i].setMag(radius);
            }
            spoke[sides] = spoke[0]; // to loop around
    
            for (int i = 0; i < spokes; i ++) { // set up spokes non used spokes
              v[i] = PVector.fromAngle(thetaSpokes * i); 
              v[i].setMag(radius); // ful length so overlaps side
              if ((i * thetaSpokes) > (currentSpoke * theta)) { // if current angle past current used spoke, move on to next spoke 
                currentSpoke ++;
              }
              overlap = lineIntersection(spoke[currentSpoke], spoke[currentSpoke-1], origin, v[i]);
              if (overlap !=null) { 
                float distance = dist(overlap.x, overlap.y, 0, 0);
                v[i].setMag(distance);
              }
            }
          }
        }
    
        PVector lineIntersection(PVector p1, PVector p2, PVector p3, PVector p4) {
          PVector b = PVector.sub(p2, p1);
          PVector d = PVector.sub(p4, p3);
    
          float b_dot_d_perp = b.x * d.y - b.y * d.x;
          if (b_dot_d_perp == 0) { 
            println("1st overlap error");
            return null;
          }
          PVector c = PVector.sub(p3, p1);
          float t = (c.x * d.y - c.y * d.x) / b_dot_d_perp;
          if (t < 0 || t > 1) { 
            println("2nd overlap error");
            return null;
          }
          float u = (c.x * b.y - c.y * b.x) / b_dot_d_perp;
          if (u < 0 || u > 1) { 
            println(u + "<-u 3rd overlap error");
            return null;
          }
          return new PVector(p1.x+t*b.x, p1.y+t*b.y);
        }
    
  • Thanks for all the help everyone! This is definitely enough to get me started.

Sign In or Register to comment.