Lines and strokes

edited November 2016 in Questions about Code

dry-brush

Hello. Here is a little sketch. Please, draw something on canvas. The class "Brush" is generating a column of lines, which seems like points, (begin and end coordinates is the same). The question is - how to connect every point with the next corresponding point (how to make begin and end coordinates of each line not the same), to get a non-interrupted column of lines? The picture is an example what strokes I want to get (they are non-interrupted).

float rotAngle;
float brushsize=20;
Brush brush1 = new Brush();
float h;
float s;
float b;
color [] variation;


void setup()
{
  size(800,800);
  background(0);
  colorMode(HSB);
  h = random(255);
  s = random(255);
  b = random(100, 255);
}


void draw()
{
  if (mouseX != pmouseX || mouseY != pmouseY)
  {
    rotAngle = atan2(mouseY - pmouseY, mouseX - pmouseX);
  }

  if(mousePressed)
  {
    pushMatrix();
    translate(mouseX, mouseY);
    rotate(rotAngle);
    rotate(PI/2);
    translate(0-brushsize/2,0-brushsize/2);
    brush1.display();
    popMatrix();
  }
}


void mousePressed()
{
  h = random(255);
  s = random(255);
  b = random(100, 255);

  variation = new color [int(brushsize)];
  color tempcolor = color(h+random(-20,20),s+random(-20,20),b+random(-100,10));

  for(int i=0;i<int(brushsize);i++)
  {
    if(frameCount % int(random(1,10))==0)tempcolor = color(h+random(-20,20),s+random(-20,20),b+random(-100,10));
    variation[i] = tempcolor;
  }
}





class Brush
{
  void display()
    {
      for(int i=0;i<brushsize;i++)
      { 
         stroke(variation[i]);
         strokeWeight(3);
         line(0+i,0,0+i,0);
      }
    }
}
Tagged:

Answers

  • edited November 2016

    A brush is many tiny hairs, each of which draws a line.

    1. Make a sketch that draws a single continuous line -- see this example
    2. Now multiply that line while drawing at many different offsets (like many different brush hairs).

    A key issue here is you need to connect previous and next mouse positions. A second issue is that you need to check mouse variables in the draw loop if you want no line gaps.

    From pMouseX:

    You may find that pmouseX and pmouseY have different values when referenced inside of draw() and inside of mouse events like mousePressed() and mouseMoved(). Inside draw(), pmouseX and pmouseY update only once per frame (once per trip through the draw() loop). But inside mouse events, they update each time the event is called. If these values weren't updated immediately during mouse events, then the mouse position would be read only once per frame, resulting in slight delays and choppy interaction. If the mouse variables were always updated multiple times per frame, then something like line(pmouseX, pmouseY, mouseX, mouseY) inside draw() would have lots of gaps, because pmouseX may have changed several times in between the calls to line().

    If you want values relative to the previous frame, use pmouseX and pmouseY inside draw(). If you want continuous response, use pmouseX and pmouseY inside the mouse event functions.

  • i tried to do this while back - just couldn't get it to look right. Slow and not very smooth at all :-(

  • jeremydouglass, the problem is that I want this brush column to rotate like in my sketch. So, I need to do the column stand still in a separate class, ant then translate and rotate it to the mouseX and mouseY. And I can't understand how to use mouseX and pmouseX in class, if I already used them in draw().

  • edited November 2016

    Here is another variation, still can't get why there is gaps between lines, even if end coordinate becomes begin coordinate in the next frame. There must be non-interrupted line!

    float rotAngle;
    float brushsize=20;
    float h;
    float s;
    float b;
    color [] variation;
    float x1;
    float y1;
    float x2;
    float y2;
    
    
    void setup() 
    {
      size(800, 800);
      background(102);
      frameRate(50);
    }
    
    void draw() 
    {
    
      if (mouseX != pmouseX || mouseY != pmouseY)
      {
        rotAngle = atan2(mouseY - pmouseY, mouseX - pmouseX);
      }
    
    
    
      if (mousePressed == true) 
      {
        for (int i=0; i<brushsize; i++)
        {
          stroke(variation[i]);
          pushMatrix();
          translate(mouseX, mouseY);
          translate(brushsize/2,0);
          rotate(rotAngle);
          rotate(PI/2);
          line(x1+i, y1, x2+i, y2);
          popMatrix();
        }
        x1=x2;
        x1=x2;
        x2 = mouseX-pmouseX;
        y2 = mouseY-pmouseY;
      }
    }
    
    
    void mousePressed()
    {
      h = random(255);
      s = random(255);
      b = random(100, 255);
    
      variation = new color [int(brushsize)];
      color tempcolor = color(h+random(-20, 20), s+random(-20, 20), b+random(-100, 10));
    
      for (int i=0; i<int(brushsize); i++)
      {
        if (frameCount % int(random(1, 10))==0)tempcolor = color(h+random(-20, 20), s+random(-20, 20), b+random(-100, 10));
        variation[i] = tempcolor;
      }
    }
    
  • edited November 2016 Answer ✓

    @djevazi -- re-read my answer. The way you are doing it will not work.

    You are trying to use rotate; you can't. Rotate turns the entire screen. After you rotate, the previous location where you last drew and value has shifted -- the pmouse variables don't point to the right place anymore. You draw a line to the "previous" place, but there is a gap based on how much you rotated, the line doesn't connect.

    Start with continuous lines, like the official example:

    //// 2016-11-01 Processing 3.2.1
    void draw() {
      if (mousePressed == true) {
        line(mouseX, mouseY, pmouseX, pmouseY);
      }
    }
    

    Brush1

    Now add multiple brush hairs:

    //// 2016-11-01 Processing 3.2.1
    void draw() {
      if (mousePressed == true) {
        for(int i=0;i<20;i=i+2){
          line(mouseX+i, mouseY, pmouseX+i, pmouseY);
        }
      }
    }
    

    Brush2

    These hairs are horizontal, so the example looks like a rake. Now you need the hairs to each be offset according to whatever heading the mouse is moving in. Then the whole brush will appear to turn. We can calculate that: Just as with mouse and previous mouse, we also need the mouse heading and the previous mouse heading.

    //// 2016-11-01 Processing 3.2.1
    PVector mouseV, pmouseV;
    float sensativity;
    void setup(){
      mouseV = new PVector(0.5,0.5);
      sensativity = 0.05;
    }
    void draw() {
      pmouseV = mouseV.copy();
      mouseV = mouseV.lerp(new PVector(mouseY-pmouseY, mouseX-pmouseX).setMag(1),sensativity);
      if (mousePressed == true) {
        for(int i=0;i<20;i=i+2){
          line(mouseX - (i*mouseV.x), mouseY + (i*mouseV.y), pmouseX - (i*pmouseV.x), pmouseY + (i*pmouseV.y));
        }
      }
    }
    

    Brush3

    Our heading flips y,x so that the brush is held perpendicular to the direction of motion. It also uses lerp() with a sensativity amount for smoothing, so the brush direction changes gradually, not jerkily. There are many, many ways to define brushes -- but at a basic level you need to take continuous lines and then calculate some kind of line offsets.

    Finally, wrap the Brush in a class:

    Brush4

  • Cool and unexpected solution!

  • when running the second example i get "cannot convert from void to PVector"

  • edited November 2016

    @BigFred -- do you mean the third example? The second example doesn't use PVectors at all.

    I'm not sure why; all three examples run fine for me on Processing 3.2.1 and 3.2.2. Do you have a copy-paste error?

    Edit: I'm assuming the problem is that you are using Processing 2. The code has to be rewritten to work with that -- methods can't be chained, .copy() used to be called .get(), .lerp() may have changed etc.

  • edited November 2016

    @BigFred:

    Processing 2 ONLY version

    (more up-to-date version above)

    //// 2016-11-02 Jeremy Douglass -- Processing 2.2.1
    PVector mouseV, pmouseV;
    float sensativity;
    void setup(){
      mouseV = new PVector(0.5,0.5);
      sensativity = 0.05;
    }
    void draw() {
      pmouseV = mouseV.get();
      mouseV.set(mouseY-pmouseY, mouseX-pmouseX);
      mouseV.setMag(1);
      mouseV = PVector.lerp(pmouseV,mouseV,sensativity);
      if (mousePressed == true) {
        for(int i=0;i<20;i=i+2){
          line(mouseX - (i*mouseV.x), mouseY + (i*mouseV.y), pmouseX - (i*pmouseV.x), pmouseY + (i*pmouseV.y));
        }
      }
    }
    

    Screen Shot 2016-11-02 at 10.21.34

  • @jeremydouglass Yes, sorry third example.

    Brilliant !!

    Actually what you have done is forced me to migrate to processing 3 !! :-)

Sign In or Register to comment.