How to automatically morph/transform/blend from one shape to another shape

Hi everyone

I'm a total novice to Processing so maybe this is a simple Question, although it is quite difficult to frame: Is it possible to automatically complete the steps between one shape and a second shape? For example in the image below i have two drawings, and i would like to automatically fill in the gap so that the first form transforms into the second. Like you can do it with the blend tool in illustrator.

Unknown Unknown-1

Thank you for any help and happy new year!

Answers

  • Not "automatically," but it is not hard to code.

    Do you want all the shapes in the screen at the same time, or are you trying to animate between a and b, aka "tweening".

    Where is your shape data coming from -- are you drawing it in processing by defining points manually, or is there an external datafile, or are you loading SVG images...?

    One approach is to list points A1, B1, C1 (big L) and points A2, B2, C2 (little L). Loop through the three pairs of points and use lerp or PVector.lerp to calculate intermediate points between each pair.

  • edited December 2017

    thank you for your answer, i think lerp is a good hint! they should appear all at the same time - for the moment. the main-goal is a bit more complex, but i need to understand the basics first.. at the moment i am drawing the shapes manually in processing, but i'm not sure if i did the drawing «the right way» or a bit too complicated (see code below)

    int x1 = 1;
    int buchstaben2x1 = 80;
    int buchstaben2x2 = 100;
    int buchstaben2y1 = 20;
    int buchstaben2y2 = 50;
    
    int buchstaben1x1 = 20;
    int buchstaben1x2 = 60;
    int buchstaben1y1 = 20;
    int buchstaben1y2 = 80;
    
    
    void setup(){
    size ( 1200, 700);
    }
    
    
    void draw ()
    { 
    background (255, 255, 255);
    buchstabenZeichnen1();
    buchstabenZeichnen2();
    }
    
    
    
    void buchstabenZeichnen1(){
    
    beginShape (LINES);
    strokeWeight(x1); //WICHTIG PUNKT 1
    vertex (buchstaben1x1, buchstaben1y1); //linie a
    vertex (buchstaben1x1, buchstaben1y2);
    vertex (buchstaben1x1, buchstaben1y2); //linie b
    vertex (buchstaben1x2, buchstaben1y2);
    endShape (CLOSE);
    }
    
    
    void buchstabenZeichnen2() {
    
    beginShape (LINES);
    strokeWeight(x1); //WICHTIG PUNKT 1
    vertex (buchstaben2x1, buchstaben2y1); //linie a
    vertex (buchstaben2x1, buchstaben2y2);
    vertex (buchstaben2x1, buchstaben2y2); //linie b
    vertex (buchstaben2x2, buchstaben2y2);
    endShape (CLOSE);
    }
    
    
    void keyPressed() //dicke und position
    {
      if (key == 'w')
      {
        x1 = x1 + 1; 
      }
      if (key == 's') {
        x1  = x1 - 1;  
      }
    
      if (key == 'o')
      {
        buchstaben2y1 = buchstaben2y1 + 5; 
        buchstaben2y2 = buchstaben2y2 + 5;
      }
      if (key == 'l') {
        buchstaben2y1 = buchstaben2y1 - 5;
        buchstaben2y2 = buchstaben2y2 - 5;  
      }
    
    }
    
  • Edit post, highlight code, press Ctrl-o to format

  • Go over the example for PShape.getVertex() to get started with using PVertex.lerp() on PShape:

    There are some tricks to using vertex data inside PShapes -- for example, transforms and rotations etc. on PShapes persist, but they aren't applied directly to the vertex value, which can be surprising when you retrieve a vertex and it is not the same as where it is drawn on the screen.

    Here is a demo that creates PShape tweens. It is based on your approach -- to get started. Another approach would be to keep your shape data in PVector[] lists directly (if appropriate) and skip using PShape entirely.

    /**
     * LerpShape
     * Create tweens between two simple PShapes using their vertex lists.
     * Jeremy Douglass 2017-12-29 Processing 3.3.6
     **/
    PShape bigL;
    PShape smallL;
    float steps;
    
    void setup() {
      size(200, 200);
      frameRate(10);
    
      // create first shape
      bigL = createShape();
      bigL.beginShape();
      bigL.noFill();
      bigL.vertex(0, 0);
      bigL.vertex(0, 50);
      bigL.vertex(50, 50);
      bigL.endShape();
    }
    
    void draw() {
      background(255);
    
      translate(10,10);
    
      // create second shape with mouse, time
      smallL = createShape();
      smallL.beginShape();
      smallL.noFill();
      smallL.vertex(mouseX, mouseY);
      smallL.vertex(mouseX, mouseY + second()%10*2);
      smallL.vertex(mouseX + second()%10*2, mouseY + second()%10*2);
      smallL.endShape();
    
      // draw tweens between shapes (red)
      stroke(255,0,0);
      // animate by drawing a different number of tweens eac 
      steps = frameCount%10;
      for(int i=0; i<=steps; i++){
        // draw the next tween
        shape(lerpShape(bigL, smallL, i/steps));
      }
    
      // draw original shapes (black)
      stroke(0);
      shape(bigL);
      shape(smallL);
    }
    
    /**
     * Takes two shapes, return a new shape of interpolated.
     * May give unexpected results if two shapes do not have
     * the same number of points.
     */
    PShape lerpShape(PShape s1, PShape s2, float amt){
      PShape tween = createShape();
      PVector v1;
      PVector v2;
      PVector v3;
      // build a new shape between each pair of points
      tween.beginShape();
      tween.noFill();
      for (int i = 0; i < s1.getVertexCount(); i++) {
        v1 = s1.getVertex(i);
        v2 = s2.getVertex(i);
        v3 = PVector.lerp(v1, v2, amt);
        tween.vertex(v3.x, v3.y);
      }
      tween.endShape();
      return tween;
    }
    

    LerpShape--screenshot

  • Thank you so much, that helps a lot! Very nice of you to create a demo - this way it is much easier to understand how everything works. happy day!

Sign In or Register to comment.