how to make the brush stroke change based on the speed you draw a line

edited July 2016 in How To...

I am working on an drawing mobile app with which you can scribble graffiti , now I encounter a problem, how to make the brush stroke change based on the speed I draw,that means when I draw very fast, the line can be thin, and vice verse, it would be thick when drawing slowly,I used the processing to program, i thought about some ideas about how to solve this problem, but unluckily most of them turned out to be not correct and can not reach my requirement, I am here searching for some good solutions that can help me deal with the problem, appreciate for any suggestions.if you have any good suggestions or similar experience you can also send email to me, my email address is jinshanliu08@gmail.com. Thanks again.

Answers

  • Answer ✓

    (duplicate. see other thread)

  • if(mousePressed ) { distance=((pmouseX-mouseX))+((pmouseY-mouseY)); if(distance!=0)ellipse(mouseX,mouseY,10/distance,10/distance); //line(mouseX, mouseY, pmouseX, pmouseY);

      }
    

    stroke(#AEC4C6); strokeWeight(7);

  • I also use line(mouseX,mouseY, pmouseX, pmouseY); but I do not know with this, how the change the thickness of the brush stroke based on the speed that you paint

    and for the former one, although it can change the thickness,but it is not smooth and continuous when user draw quickly, it turns out to be a list of ellipse.

    so finally my purpose is that I want to make the stroke change based on the speed that i draw with smooth and continuous line.

  • Answer ✓
    void setup() {
      size(640, 360);
      background(102);
    }
    
    void draw() {
      // Call the variableEllipse() method and send it the
      // parameters for the current mouse position
      // and the previous mouse position
      variableEllipse(mouseX, mouseY, pmouseX, pmouseY);
    }
    
    
    // The simple method variableEllipse() was created specifically 
    // for this program. It calculates the speed of the mouse
    // and draws a small ellipse if the mouse is moving slowly
    // and draws a large ellipse if the mouse is moving quickly 
    
    void variableEllipse(int x, int y, int px, int py) {
    
      float speed = abs(x-px) + abs(y-py);
      strokeWeight(speed);
    
      // ellipse(x, y, speed, speed);
    
      stroke(255);
      if (mousePressed == true) {
        line(mouseX, mouseY, pmouseX, pmouseY);
      }
    }
    
  • Thanks man,that works well,I would like to show you the app i will create after finish

  • Hi chrisir, the last question for the final brush stroke the last brush that I want to create is one with water droplets,that means after I draw the line, there comes out a short line slowly from the line that I drew, it is random,but when I draw fast, the droplets are little, otherwise when I draw slowly, the droplets are much more. as it seems that the picture can not be viewed so I hope you can understand me.

  • Answer ✓

    yes

    show your attempt

  • edited July 2016

    here is a small example with ideas from the forum - not by me...

    float startX=-1, startY=-1;
    
    // constants 
    final int lineMode   = 0;
    final int circleMode = 1;
    final int rectMode   = 2;
    final int brushMode  = 3;
    int mode = brushMode; // current 
    String[] description={"line mode", "ellipse mode", "rect mode", "points mode"}; 
    
    void setup() {
      size(900, 500);
      background(255);
    } // func 
    
    void draw() {
    
      showText();
    
      if (mode==lineMode) {
        if (startX>=0) {
          stroke(random(pmouseX), random(pmouseY), abs(pmouseX-pmouseY));
          line(startX, startY, pmouseX, pmouseY);
        }
      } else if (mode==circleMode) {
        if (startX>=0) {
          noFill();
          stroke(random(pmouseX), random(pmouseY), abs(pmouseX-pmouseY));
          ellipse (startX, startY, mouseX-startX, mouseY-startY);
        }
      } else if (mode==rectMode) {
        if (startX>=0) {
          noFill();
          stroke(random(pmouseX), random(pmouseY), abs(pmouseX-pmouseY));
          rect (startX, startY, mouseX-startX, mouseY-startY);
        }
      } else if  (mode==brushMode) {
        if (startX>=0) {
          noFill();
          for (int i=0; i<11; i++) {
            float r=random(18);
            float angle = random(TWO_PI); 
            float x1=(r*cos(angle)) + mouseX;
            float y1=(r*sin(angle)) + mouseY;
            stroke(255, 0, 0); 
            point(x1, y1);
          }
        }
      } else {
        println("Error on mode : 176");  
        exit();
      }
    } // func 
    
    // -----------------------------------------------------------------
    
    void showText() {
      noStroke(); 
      fill(111);
      rect(0, 0, width, 20);
    
      fill(255);
      text("Modes: e (ellipses), r (rectangles), l (lines), b (points). Current mode is "
        +description[mode]
        +". Mouse to draw. c to clear. q to quit.", 
        14, 14);
    }
    
    void keyPressed()
    {
      if (keyPressed) {
        if (key == 'c') {
          background(255);
        } else if (key==ESC) {
          key=0;
          startX=-1;
        }
        // ---------------------- modes 
        else if (key=='e') {
          mode = circleMode;
          println ("mode = circle Mode");
          startX=-1;
        } else if (key=='l') {
          mode = lineMode;
          println ("mode = line Mode");
          startX=-1;
        } else if (key=='r') {
          mode = rectMode;
          println ("mode = rect Mode");
          startX=-1;
        } else if (key=='b') {
          mode = brushMode;
          println ("mode = brush Mode");
          startX=-1;
        }
    
        // ---------------------------- quit 
        else if (key=='q') {
          exit(); // quit
        }
      } //
    } // func 
    
    void mousePressed() {
      startX=mouseX;
      startY=mouseY;
    }
    
    void mouseReleased() {
      startX=-1;
      startY=-1;
    }
    //
    
  • edited July 2016 Answer ✓

    this basically draws small explosions (of colorful and growing lines) along the mouse path

    The speed of the mouse movement is again used to determine the number or length of the lines

    /* OpenProcessing Tweak of *@*http: // www.openprocessing.org/sketch/77863*@* */
    /* !do not delete the line above, required for linking your tweak if you upload again */
    //
    // idea from 
    // http: // forum.processing.org/topic/need-help-adding-shooting
    // with help from 
    // http: // forum.processing.org/topic/have-eyes-follow-mouse
    //
    // for explosions (these are actually Shrapnels)
    ArrayList <Explosion> missiles; 
    
    // speed of the mouse
    float speed;
    
    // ------------------------------------------------------------------
    
    void setup() {
      size(1600, 980);
      background(100); 
    
      // explosions: Create an empty ArrayList
      missiles = new ArrayList<Explosion>();
      // cursor(CROSS);
    }
    
    void draw() {
      ExplosionManager();
    
      speed = abs(mouseX-pmouseX) + abs(mouseY-pmouseY);
    
      // drawing: if mouse pressed and new position of the mouse
      if (mousePressed && (mouseX!=pmouseX|| mouseY!=pmouseY)) {
        explode(mouseX, mouseY);
      }
    }
    
    // ---------------------------------------------------------------
    
    void keyPressed() {
      background(100);
    }
    
    void explode (float x, float y) {
    
      // explode!
    
      // This is really interesting, when you want to change the 
      // forms that are shown. 
    
      // some experiments here: 
      // int maxMissiles = int(random(0, 12));
      int maxMissiles = int(random(0, speed));
      // int maxMissiles = int(speed);
    
      for (int i=0; i<maxMissiles; i+=1) {   
        // this is where explode missile/shrapnel object is created
        // and added to the missiles arrayList.
    
        // It is small (4, not in use!!!!) and life decrease is .72 (how fast it dies, this determines the radius),
        // line (false or true).
        // here also speed is used. 
        float factor=12;// e.g. 1 or 2 or 3
        missiles.add( new Explosion(
          x, y, 
          random(-speed/factor, speed/factor), random(-speed/factor, speed/factor), 
          4, .72, true)); 
    
        // BEST VERSION 
        //missiles.add( new Explosion(
        //  x, y, 
        //  random(-speed/factor, speed/factor), random(-speed/factor, speed/factor), 
        //  4, .72, true)); //
    
        // // old 2 
        //missiles.add( new Explosion(
        //      x, y, 
        //      random(-1.3, 1.3), random(-2.7, 2.7), 
        //      4, .72, false)); //
    
    
        ////old:
        //missiles.add( new Explosion(
        //random(x, x), random(y+9, y+12), 
        //random(-1.3, 1.3), random(-2.7, .6), 
        //4, .72, false)); //
      }
    } //
    
    void ExplosionManager() {
      // show the lines of the explosion.
      // This is not really interesting. 
    
      // this is a for loop for the Shrapnels.
      for (Explosion m : missiles) {
        m.decreaseLife(); // call the decrease life (for explosion)
        m.fly();          // call the "fly" method of the missile
        m.display();      // call the "display" method of the missile
      }
      //
      // remove dead Shrapnels (you need to have a conventional backward for-loop here)
      for (int i=missiles.size()-1; i>=0; i--) {
        Explosion m = (Explosion) missiles.get(i);
        if (m.dead) {
          missiles.remove(i);
        }
      }
    } // func
    
    // ===============================================================
    // the Explosion class
    
    class Explosion {
    
      float startlocX, startlocY; // start pos
      float x, y;          // current pos
    
      float xvel, yvel;    // velocity
    
      float sizeMissile;   // size for rect
      float life=20;       // how much lifes does it have
      float lifeDecrease;  // remove lifes
      boolean dead=false;  // is it alive?
      boolean withLine;    // draw line? yes / no
    
      // random color 
      color colorExplosion=color(random(255), random(255), random(255));
    
      //
      // constructor
      Explosion(
        float _startlocX, float _startlocY, 
        float _xvel, float _yvel, 
        float _size, 
        float _lifeDecrease, 
        boolean _withLine)
      {
        startlocX = _startlocX;
        startlocY = _startlocY;  
    
        x = startlocX;
        y = _startlocY;
    
        xvel = _xvel;
        yvel = _yvel;
    
        sizeMissile = _size;
        lifeDecrease=_lifeDecrease;
    
        withLine=_withLine;
      }  // constructor
    
      void display() {
        if (withLine) {
          stroke(colorExplosion);
          line(startlocX, startlocY, x, y);
        } else {
          noStroke();
          fill(colorExplosion);
          rect(x, y, sizeMissile, sizeMissile);
        }
        sizeMissile-=.07;
      } // method
    
      void fly() {
        x += xvel;
        y += yvel;
      } // method
    
      void decreaseLife() {
        life-=lifeDecrease;
    
        // check wall collision
        if (x<0) 
          dead=true;
        if (y<0) 
          dead=true;
        if (x>width) 
          dead=true;
        if (y>width) 
          dead=true;
    
        if (life<=0 || sizeMissile<=0) {
          dead=true;
        }
      } // method
      //
    } // class
    // =============================================
    
  • that is cool, I will try that I tried the code you posted and there is a little problem, which is the line is not smooth even it is continuous, like it is a jointed line, as it is a painting brush stroke, i want to make it as real as possible.so I wonder if there is any other methods to make it smooth.

  • I wonder that too

  • Answer ✓

    when you wrote

    little problem, which is the line is not smooth even it is continuous

    you were referring to the sketch with strokeWeight I guess.

    Here comes a improved version which uses damping when it comes to changing the strokeWeight

    The damping value is .0178, meaning 0.0178, the smaller this number, the slower the strokeWeight changes (it can get still very high but slowly so). Because strokeWeight changes slower I hope it looks more smooth.

    hope this helps.

    Chrisir

    // mouse speed controls line thickness 
    
    // Version with damping
    
    float currentStrokeWeight=1.0;
    
    void setup() {
      size(640, 360);
      background(102);
    }
    
    void draw() {
      // Call the function and send it the
      // parameters for the current mouse position
      // and the previous mouse position
      variableEllipse(mouseX, mouseY, pmouseX, pmouseY);
    }
    
    
    // The simple function was created specifically 
    // for this program. It calculates the speed of the mouse
    // and draws a small line if the mouse is moving slowly
    // and draws a thicker line if the mouse is moving quickly 
    
    void variableEllipse(int x, int y, int px, int py) {
    
      float speed = abs(x-px) + abs(y-py);
    
      currentStrokeWeight+=(speed-currentStrokeWeight)*.0178; // damping 
    
      strokeWeight(currentStrokeWeight);
    
      // ellipse(x, y, speed, speed);
    
      stroke(255);
      if (mousePressed == true) {
        line(mouseX, mouseY, pmouseX, pmouseY);
      }
    }
    
  • wow,this is amazing,that is much better now, I think it can be the final version then. Thank so much, now i have create 6 brush strokes for my project. I would like to show you later.

  • Answer ✓

    Please do so

  • Can I have your email so that you can have a look at the brush that I created.

  • here a color selector from the forum (not by me!!!)

    ColorPicker cp;
    boolean modeColorSelector=true; 
    color col1; 
    
    void setup() 
    {
      size( 500, 500 );
      frameRate( 100 );
    
      cp = new ColorPicker( 10, 10, 400, 400, 255 );
    }
    
    void draw ()
    {
    
    
    
      if (modeColorSelector) {
        background( 80 );
        cp.render();
        fill(255); 
        text("Select a color. Hit d to draw with this color", 
          20, height-9);
      } else 
      {
    
        col1=cp.getColor(); 
        stroke(col1); 
        line(mouseX, mouseY, 
          pmouseX, pmouseY);
        fill(111);
    
        noStroke(); 
        rect(0, height-21, width, height);  
        fill(255); 
        text("Hit d to choose another color", 
          20, height-9);
      }
    }
    
    void keyPressed() {
      if (key=='d') { 
        modeColorSelector=!modeColorSelector;
        background( 80 );
      }
    }
    
    // ================================================
    
    public class ColorPicker {
      int x, y, 
        w, h, 
        c;
      PImage cpImage;
    
      public ColorPicker ( int x, int y, int w, int h, int c )
      {
        this.x = x;
        this.y = y;
        this.w = w;
        this.h = h;
        this.c = c;
    
        cpImage = new PImage( w, h );
    
        init();
      }
    
      private void init ()
      {
        // draw color.
        int cw = w - 60;
        for ( int i=0; i<cw; i++ ) 
        {
          float nColorPercent = i / (float)cw;
          float rad = (-360 * nColorPercent) * (PI / 180);
          int nR = (int)(cos(rad) * 127 + 128) << 16;
          int nG = (int)(cos(rad + 2 * PI / 3) * 127 + 128) << 8;
          int nB = (int)(Math.cos(rad + 4 * PI / 3) * 127 + 128);
          int nColor = nR | nG | nB;
    
          setGradient( i, 0, 1, h/2, 0xFFFFFF, nColor );
          setGradient( i, (h/2), 1, h/2, nColor, 0x000000 );
        }
    
        // draw black/white.
        drawRect( cw, 0, 30, h/2, 0xFFFFFF );
        drawRect( cw, h/2, 30, h/2, 0 );
    
        // draw grey scale.
        for ( int j=0; j<h; j++ )
        {
          int g = 255 - (int)(j/(float)(h-1) * 255 );
          drawRect( w-30, j, 30, 1, color( g, g, g ) );
        }
      }
    
      private void setGradient(int x, int y, float w, float h, int c1, int c2 )
      {
        float deltaR = red(c2) - red(c1);
        float deltaG = green(c2) - green(c1);
        float deltaB = blue(c2) - blue(c1);
    
        for (int j = y; j<(y+h); j++)
        {
          int c = color( red(c1)+(j-y)*(deltaR/h), green(c1)+(j-y)*(deltaG/h), blue(c1)+(j-y)*(deltaB/h) );
          cpImage.set( x, j, c );
        }
      }
    
      private void drawRect( int rx, int ry, int rw, int rh, int rc )
      {
        for (int i=rx; i<rx+rw; i++) 
        {
          for (int j=ry; j<ry+rh; j++) 
          {
            cpImage.set( i, j, rc );
          }
        }
      }
    
      public void render ()
      {
        image( cpImage, x, y );
        if ( mousePressed &&
          mouseX >= x && 
          mouseX < x + w &&
          mouseY >= y &&
          mouseY < y + h )
        {
          c = get( mouseX, mouseY );
        }
        fill( c );
        rect( x, y+h+10, 20, 20 );
      }
    
      color getColor() {
        return c;
      }
    }
    //
    
Sign In or Register to comment.