How can i create Brushes for drawing?

I want to make brushes for my drawing software, i have code, but its kinda bad and not working correctly.
The problem is if i put down a image of the brush every frame, it will not work when moving the mouse fast. I tried fixing that. Heres my (not fully working) code.

The image is just a gradient from middle.

It is working kinda fine, but between the mouse beeing moved too fast and slow enought (there are 2 functions that switch) there is some error.

thanks anyone who is helping!

int dist, distnow, max = 10, size = 35;

boolean mouseClick = false;
boolean pressed = false;
PVector mouseClickA = new PVector(0,0);
PVector mouseClickB = new PVector(0,0);

PImage airbrush;

void setup() {
  size(960,540);
  noStroke();
  frameRate(255);
  airbrush = loadImage("b1.png");
}

void draw() { 
  dist += (int)dist(mouseX,mouseY,pmouseX,pmouseY);
  distnow = (int)dist(mouseX,mouseY,pmouseX,pmouseY);
  
  if(mousePressed) {
    line(mouseX,mouseY,pmouseX,pmouseY);
    mouseClickA.x = mouseX;
    mouseClickA.y = mouseY;
    mouseClickB.x = pmouseX;
    mouseClickB.y = pmouseY;
    fill(0,25);
    noStroke();
    if(dist > max && distnow-5 < max) image(airbrush,mouseX,mouseY,size,size);//circle(mouseX,mouseY,size);
    if(distnow > max) {
      dottedLine(mouseClickA,mouseClickB, max+3);
      image(airbrush,mouseX,mouseY,size,size);//circle(mouseX,mouseY,size);
    }
  }
  
  if(dist > max) dist = 0;
  
  if(keyPressed) background(#CCCCCC);
  
  fill(255);
  rect(0,0,width-1,50);
  fill(0);
  text(dist+"\n"+distnow,10,20);
  text(frameRate,50,20);
}

void dottedLine(PVector posA, PVector posB, int distance) {
  posA = posA.copy();
  posB = posB.copy();
  PVector direction = posB.sub(posA);
  int len = (int) (direction.mag() / distance);
  direction.normalize();
  direction.mult(distance);
  fill(0,25);
  for (int i = 0; i < len; i++) {
    posA.add(direction);
    image(airbrush,posA.x,posA.y,size,size);//circle(posA.x,posA.y,size);
  }
}
1 Like

hm, not sure what you mean.

We don’t have your brush image, but I used a point() instead.

Is it a speed issue?

Hint: to use image() command with 5 parameters is costly.

  • Instead use image always with 3 parameters.

  • use resize in setup - only once

  • make sure that the image is only drawn once per loop. I see it 3 times in your code?

Also, it might be faster to store mouseX and mouseY in a variable and use those instead.

Remark.
dist, distnow; should be float

Chrisir



//

float dist, distnow;
int max = 10, size = 35;

boolean mouseClick = false;
boolean pressed = false;
PVector mouseClickA = new PVector(0, 0);
PVector mouseClickB = new PVector(0, 0);

PImage airbrush;

void setup() {
  size(960, 540);
  //noStroke();
  stroke(0);
  //frameRate(255);
  //  airbrush = loadImage("b1.png");
}

void draw() {

  stroke(0);

  dist    += (int)dist(mouseX, mouseY, pmouseX, pmouseY);
  distnow = (int)dist(mouseX, mouseY, pmouseX, pmouseY);

  if (mousePressed) {
    line(mouseX, mouseY, pmouseX, pmouseY);
    mouseClickA.x = mouseX;
    mouseClickA.y = mouseY;
    mouseClickB.x = pmouseX;
    mouseClickB.y = pmouseY;
    fill(0, 25);
    //  noStroke();
    //  if (dist > max && distnow-5 < max) image(airbrush, mouseX, mouseY, size, size);//circle(mouseX,mouseY,size);
    if (dist > max && distnow-5 < max)
      point(mouseX, mouseY);

    if (distnow > max) {
      dottedLine(mouseClickA, mouseClickB, max+3);
      //image(airbrush, mouseX, mouseY, size, size);//circle(mouseX,mouseY,size);
      point(mouseX, mouseY);
    }
  }

  if (dist > max)
    dist = 0;

  if (keyPressed)
    background(#CCCCCC);

  fill(255);
  noStroke();
  rect(0, 0, width-1, 50);
  fill(0);
  text(dist+"\n"+distnow, 10, 20);
  text(frameRate, 50, 20);
}

// --------------------------------------------------------------------

void dottedLine(PVector posA, PVector posB, int distance) {
  posA = posA.copy();
  posB = posB.copy();
  PVector direction = posB.sub(posA);
  int len = (int) (direction.mag() / distance);
  direction.normalize();
  direction.mult(distance);
  fill(0, 25);
  for (int i = 0; i < len; i++) {
    posA.add(direction);
    point( posA.x, posA.y );
    // image(airbrush, posA.x, posA.y, size, size);//circle(posA.x,posA.y,size);
  }
}

1 Like

Try making the image smaller if possible, it makes a big difference. You could also try using it as a mouse cursor icon. Just a thought…

1 Like

Yes and no.

  • Yes: cursor() / Reference / Processing.org

  • No: I think actually, OP is using the brush as staying on the canvas as being the actual stroke. This won’t be possible with mouse cursor because it’s not permanent.


Here is another brush - maybe for Calligraphy (ONE MORE Sketch BELOW)

float startX=-1, startY=-1;

ArrayList<PVector> list = new ArrayList();

//----------------------------------------------------------------------------------------------------

void setup() {
  size(900, 500);
  background(255);
  stroke(255, 0, 0); // RED
} // func setup()

void draw() {
  showText();

  if (startX>=0) {
    for (int i=0; i<list.size(); i++) {
      PVector pv=list.get(i);

      stroke(255, 0, 0); // RED
      line(pv.x, pv.y,
        pv.x+(mouseX-pmouseX), pv.y + (mouseY-pmouseY));

      list.get(i).x += mouseX-pmouseX;
      list.get(i).y += mouseY-pmouseY;
    }
  }
} // func draw()

// -----------------------------------------------------------------

void showText() {
  noStroke();
  fill(111);
  rect(0, 0, width, 20);

  fill(255);
  text("Mouse to draw. The slower you move the mouse, the more points you get. c to clear. q to quit.",
    14, 14);
}

void keyPressed() {
  if (key == 'c') {
    background(255);
  } else if (key==ESC) {
    key=0;
    startX=-1;
  } else if (key=='q') {
    exit(); // quit
  }
} // func

void mousePressed() {
  startX=mouseX;
  startY=mouseY;

  list.clear();

  // how many points we draw per frame
  for (int i=0; i<166; i++) {
    // float radius=random(18);
    float radius=random(0, 28);
    if (random(1)<0.5)
      radius*=-1;

    float angle = random(TWO_PI);
    float x1=radius*cos(angle) + mouseX;
    float y1=radius*sin(angle) + mouseY;
    if (dist(x1, 0, mouseX, 0) < 3) {
      list.add(new PVector(x1, y1));
    }
  } // for
}

void mouseReleased() {
  startX=-1;
  startY=-1;
}
//


And another Paint program



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 draw()

// -----------------------------------------------------------------

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;
}
//

2 Likes

Yes, you are right. I meant it more like an addition to the existing sketch, just to spice it up a little. Like e.g. in Photoshop when the tool for drawing/painting is visible all the time…

2 Likes

Consider lerping between the two points:

PVector v1, v2, v3;

void setup() 
  {
  size(500, 500);
  v1 = new PVector(0.0, 0.0);
  v2 = new PVector(width, height);
  v3 = new PVector(0, 0);
  noLoop();
  }

void draw() 
  {
  float dist = PVector.dist(v1, v2);
  println(dist);
  for(int i=0; i<100; i++)
    {
    v3 = PVector.lerp(v1, v2, i/100.0);
    println(i, v1, v2, v3);
    circle(v3.x, v3.y, 10);
    }
  }

:)

is using vectors better than points?