How can I make an animation like this? (calligraphy)

edited February 2017 in How To...

I want to make an animation like this. Any comment?

Tagged:

Answers

  • That would be very difficult, I guess. Who made that animation and how?

  • edited January 2017

    @Lord_of_the_Galaxy: Who made that animation and how?

    I do not know who and how it was made. But I found it here.

  • If you have a path for the letter you want to draw, this should not be too hard. Here is an example that creates a similiar effect with the mouse-path.

    int offsetX = 10;
    int offsetY =20;
    
    void setup() {
      size(500, 500);
      noStroke();
      fill(0);
    }
    
    void draw() {
    
      int x1 = mouseX;
      int y1 = mouseY;
      int x2 = pmouseX;
      int y2 = pmouseY;
    
      quad(x1, y1, x2, y2, x2+offsetX, y2+offsetY, x1+offsetX, y1+offsetY);
    }
    
  • edited January 2017

    @BabluKumar --

    Something like this approach might work:

    1. CalligStroke contains an ArrayList< PVector > holding an even number of vertices such that if you createShape and load 4, 6, 8, ... n-4, n-2, n vertices to show less or more of the stroke. You can pass it a 0-1 float argument (amt) and it returns a PShape.
    2. CalligChar contains an ArrayList< CalligStroke >. Pass it a 0-1 float argument and it returns a list of PShapes for drawing.

    The logical thing would be to do all this in PShape -- but they are (sadly) largely immutable.

    P.S. Just realized this was a p5.hs question -- my suggestion was in terms of Processing (Java) but the approach should also work for p5.js

  • edited February 2017

    The code by @benja.... You could use that.
    Draw the letter by hand (mouse) on the screen. Look at it. You can save it by saving the corresponding vertices of the quad(s).

    Another option would be to define the letter in a method similar to what @jeremydouglass says. It's much better, but also much slower.

  • Thanks to @benja, @jeremydouglass and @Lord_of_the_Galaxy for your comments. But I unfortunately did not understand what @jeremydouglass wanted to say. I have never used PShapes and CalligStroke.

    I am still thinking of @benja 's code as to how I can make use of that.

  • @Lord_of_the_Galaxy: Not yet. Just thinking about it.

  • edited February 2017

    OK, best of luck.
    Tips-

    You need an ArrayList of type PVector, which adds to itself the mouse vector (=new PVector(mouseX, mouseY)) every time through draw() if the mouse is pressed, and some random but impossible for mouse vector if mouse isn't pressed.

  • Thanks for shedding your insights.

  • @BabluKumar===

    this is not an animation this is a .gif

  • edited February 2017

    @akenaton "animation" is the common word used for all .gif files (even if it isn't the proper word), and also short computer generated video files of any type.

  • edited February 2017

    @Lord_of_the_Galaxy===

    you are playing with words.

    you said: > That would be very difficult, I guess. Who made that animation and how?

    I answered = this is a .gif and NOTHING ELSE (easy to do with photoshop);

  • @akenaton Then you should have told that to me.
    Even a .gif qualifies as an animation. But I didn't have any knowledge of Photoshop, if you would have told that it is easy to do in Photoshop then I would've understood what you meant.

  • edited February 2017

    @Lord_of_the_Galaxy===

    In some sense each numerical object is an animation as soon as it is on screen. In this sense .gif, .mov, .jpg, .png... are all animations.

    But in my mind and here (this forum!) we are speaking about animations in a more restrictive sense, for which in this moment i cannot find what word, even "qualified" to use, because saying "coded animations" or " java coded..." or "C++ coded" animations can be applied also to .mov or...: though created by photoshop and its algorithms or quicktime or... they are coded and C++ or java or...coded.

    Nevertheless there are (it seems to me) 3 big differences: interactivity, use of random, non-linearity; of course these differences can be not exploited or present: you can make some kind of .mov loading 3000 images and putting them on screen without any interactivity, any random (from1 to 3000),any non linearity. In the same way you can create some kind of .gif (with png and alpha) but what interest???

    As for the initial question: yes we can code something which looks like the linked .gif: as for me 5 or 6 hours of work with P5; but 15' with photoshop. So, if it is for "exercise" ok, if it is for some real project, no!

  • Wow. You must be an expert at Photoshop (can you even make a changing image in Photoshop, I thought it was purely for editing photos?) (or I am just a noob, I've never used it). In that case my recommendation changes to Photoshop - you can create an animation (in my sense) using Photoshop for each letter and display it as you want.

  • @Lord_of_theGalaxy===

    yes you can create an animation- in your "wide" sense with photoshop (or gimp) (or gif, or .mov) but you cannot in my "restrictive" sense...

  • @BabluKumar -- returning to your question:

    But I unfortunately did not understand what jeremydouglass wanted to say. I have never used PShapes and CalligStroke.

    To be clear, PShape is a useful Processing object for storing a shape. My proposal was also to use classes to create custom objects. There is no such thing in Processing as a CalligStroke -- you would use class CalligStroke to create your own -- or name it something else, I was just imagining that as a name for a "Calligraphy Stroke".

    If you are curious, check out the PShape tutorial and reference page, then the Objects tutorial and the class reference page:

  • @BabluKumar Learn Photoshop and use it to create the "animations" you need. Then just play it on screen.

  • edited February 2017 Answer ✓

    @Lord_of_the_Galaxy, @BabluKumar

    Though it s more quick with Photoshop, you can of course write a code for getting the same result. You dont need to use PShape. Code below (i have used a screen shot from the .gif, it is this one, called cal.png

        PImage img;
        color noir;
        PVector[] Noirs;
        ArrayList<Integer> tabEdgesX = new ArrayList <Integer> ();
        ArrayList<Integer> tabEdgesY = new ArrayList <Integer> ();
        int nbre = 0;
        int pointrouge = 0;
        boolean aller = true;
        boolean monte = true;
        float varY=0f;
        float valeurXmin;
        float valeurXmax;
        float valeurYmin;
        float valeurYmax;
        int poi = 0;
        float initValx;
        float initValy;
    
    
    
        void setup(){
    
          size(800,600);
          background(255,255,255);
          img = loadImage("cal.png");
    
          //This image is from the .gif, a screen shot that i have imported in photoshop to smooth it && get rid of the pink area which does not matter here
    
          //but if you want to get it more simply you have to use some indi font 'Devanagary' for instance
          //because this "shape"is a char....
    
          // As the screen shot was made randomly the image is black, white and gray
          // firstly i make it only black (as at the end of the .gif
    
          noir = color(0,0,0);
          Noirs = new PVector[img.width*img.height];
          for(int i = 0; i<img.width-1;i++){
         for(int j =0; j<img.height-1;j++){
        color coul = img.get(i,j);
        if( coul != noir && coul != color(255,255,255)){
         //coul=noir;
         img.set(i,j, noir);
         PVector pv = new PVector(i,j);
    
         Noirs[nbre] = pv;
         tabEdgesX.add(int(pv.x));
         tabEdgesY.add(int(pv.y));
         nbre++;
    
    
             }else if(coul == noir){
                PVector pn = new PVector(i,j);
               Noirs[nbre] = pn;
         tabEdgesX.add(int(pn.x));
         tabEdgesY.add(int(pn.y));
             }
        }
    
        //  at this point i have all black pixels in an array and x or y values in 2 others
        }
    
        pointrouge = nbre;
        // now to boost up the process i get the max && min values for edges
        // because the white pixels do not matter
    
        int[] da = new int[tabEdgesX.size()];
        for(int n = 0; n<da.length; n++){
         da[n] = tabEdgesX.get(n); 
        }
    
        valeurXmin = min(da);
        valeurXmax = max(da);
        initValx = valeurXmin;
    
        int[] dY = new int[tabEdgesY.size()];
        for(int n = 0; n<dY.length; n++){
         dY[n] = tabEdgesX.get(n); 
        }
        valeurYmin = min(dY);
        valeurYmax = max(dY);
        initValy = valeurYmin;
    
        image(img,200,200);
    
        //noLoop();// here uncommenting you can see the result
        }
    
        void draw(){
    
         //FIRST SOLUTION (with PVectors) too slow and not the same as the .gif
    
         //UP/DOWN
    
          if(aller){
    
    
            PVector p= new PVector();
    
           p.x = Noirs[poi].x;
            p.y = Noirs[poi].y;
            color rouge = color(255,0,0);
    
           img.set(int(p.x),int(p.y),rouge) ;
          }
         image(img,200,200);
    
            if(poi<nbre-1){
    
          poi++;
          }else{
    
            poi=0;
            aller = false;
    
          }
    
    
          if(!aller){
    
            //DOWN UP
    
             int i = pointrouge-1;
            PVector p= new PVector();
           p.x = Noirs[i].x;
           //println(p.x);
           p.y = Noirs[i].y;
           color bleu = color(0,0,255);
           img.set(int(p.x),int(p.y),bleu) ;
    
    
          //}
           image(img,200,200);
    
            if(pointrouge>1){
          pointrouge--;
    
    
        }else{
    
           pointrouge = nbre;
           poi=0;
    
           aller = true;
          }
        }
        //SECOND SOLUTION USING EDGES to SPEED UP the PROCESS and line 
        //for getting the .gif effect
        // of course if you want you can change (or add) values for colors and varY
        //of course also you can comment the first solution and get only the second one on screen!!
    
          PImage temp = img.get((int)(valeurXmin) ,(int)(varY),(int)(valeurXmax),2);
    
          temp.loadPixels();
          for(int k=0; k<temp.height*temp.width;k++){
    
              if(temp.pixels[k] != color(255,255,255)){
                if(varY <100 ){
                  if(!monte){
              temp.pixels[k] = color(255,0,0);
                  }else{
                    temp.pixels[k] = color(0,0,0);
                  }
                }else{
                  if(monte){
                  temp.pixels[k] = color(0,0,0);
                }else{
                 temp.pixels[k] = color(255,0,0);
                }
              }
    
          }
          }
          temp.updatePixels();
           image(temp,500,varY+200);
    
           line(200+valeurXmin,200+varY,200+valeurXmax,200+varY);
    
           if(initValx< valeurXmax){ 
           initValx ++;
           }else{
            initValx = valeurXmin; 
           }
    
          if(varY < valeurYmax && monte){
          varY++;
          }else{
            if(varY>valeurYmin){
           varY--; 
           monte= false;
            }else{
              monte = true;
            }
          }
            };
    
  • @akenaton: Thanks for your tremendous effort and time. I did not imagine this could be such a long code just to make a .gif image. You have really done a great job. Well, it is yet to test!

  • edited February 2017

    Very cool example, @akenaton !

    There is one drawback to this kind of approach (if I'm understanding your sketch correctly). That is that character stroke order does not move top-to-bottom, so if you are trying to animate a character in order to explain stroke order then you cannot simply "wipe" in any direction. For example, in the provided example character the top bar is added last. https://hacback17.tinytake.com/sf/MTMwNTQ5MV80ODQ5Nzgw

    An alternate approach is to specify each stroke as a child shape of a PShape, then animate by revealing the strokes one at a time. If you want to fully animate each stroke you need to do more by working with coordinate lists (as in my answer above), but this is a rough example to give the idea:

    PShape fig, s;
    
    void setup() {
      size(200, 200);
    
      // calligraphy figure
      fig = createShape();
    
      // stroke
      s = createShape();
      s.beginShape();
      s.noStroke();
      s.vertex(20,-40);
      s.vertex(30,-40);
      s.vertex(30,-10);
      s.vertex(20,-10);
      s.endShape(CLOSE);
      fig.addChild(s);
    
      // stroke
      s = createShape();
      s.beginShape();
      s.noStroke();
      s.vertex(20,-10);
      s.vertex(20,-20);
      s.vertex(-20,-20);
      s.vertex(-20,-10);
      s.endShape(CLOSE);
      fig.addChild(s);
    
      // stroke
      s = createShape();
      s.beginShape();
      s.noStroke();
      s.vertex(-50,-50);
      s.vertex(50,-50);
      s.vertex(60,-40);
      s.vertex(-40,-40);
      s.endShape(CLOSE);
      fig.addChild(s);
    
    }
    
    void draw() {
      background(0);
      translate(width/2, height/2);
      // animate showing strokes 0, 0+1, or 0+1+2
      for(int i=0; i<second()%fig.getChildCount()+1; i++){
        shape(fig.getChild(i));
      }
    }
    
Sign In or Register to comment.