Continuously update drawing during mouseDragged

I am trying to create a simple mspaint like application on the web using p5 as a learning exercise. I have provided a button to draw an ellipse on the canvas and want to show the ellipse being resized as the mouse is dragged. The final ellipse should be shown once the mouse button is released.

let penColor = '#ffffff';
let bgColor = '#000000';
let strokeWt = 4;
let globalX = 0;
let globalY = 0;

function setup() {
  createCanvas(400, 400);
  background(color(bgColor));
}

function draw() {
  stroke(penColor);
  strokeWeight(strokeWt);
  noFill();
}

function mousePressed() {
  globalX = mouseX; 
  globalY = mouseY;
}

function mouseReleased() {
  ellipseMode(CORNERS);
  ellipse(globalX, globalY, mouseX, mouseY);
}

function mouseDragged() {
  push();
  ellipseMode(CORNERS);
  ellipse(globalX, globalY, mouseX, mouseY);
  stroke(bgColor);
  ellipse(globalX, globalY, mouseX, mouseY);
  pop();
}

The code can be viewed here:

As can be seen above, the current code does give a beautiful pattern but it is not what I wanted. It also overwrites any previous ellipses drawn below, which I want to retain. I saw a reference to thread() in another post but that does not work in JS. Any ideas how this can be achieved?

Hello and welcome to the forum!

It’s great to have you here with us!

basically, use background at start of draw() and when you release mouse, put the new ellipse in an ArrayList and display the content of the ArrayList throughout.

it’s not easy

Here is an example in processing (not p5) with lines

to draw a line, click mouse once, release mouse, move mouse, click mouse again and release mouse

Warm regards,

Chrisir



// to draw a line click mouse once, release mouse, click mouse again and release mouse 

// The coordinates of the lines: each line goes "from" a point "to" a point (PVector).

// The lists are parallel in that sense that the elements of both lists together make a line: 
// the 0th element of both lists make one line, the 1st element of both lists make one line, the 2nd etc. 
ArrayList<PVector> from = new ArrayList(); 
ArrayList<PVector> to = new ArrayList();

// necessary to know if the mouse enters a "from" or a "to" point of a line
int situation=0;

// -------------------------------------------------------------------------
// processing core functions  

void setup() {
  size(600, 1000);
  stroke(255);
}//func

void draw() {
  background (0); 
  stroke(255);

  // show all stored lines 
  for (int i = 0; i < to.size(); i++) {
    PVector currentFrom = from.get(i);
    PVector currentTo   = to.get(i);
    // the line 
    linePV ( currentFrom, 
      currentTo );
  }//for

  // we show current line 
  if (situation==1) {
    stroke(0, 255, 0); 
    linePV(from.get(from.size()-1), 
      new PVector(mouseX, mouseY));
  }
}//func 

// -------------------------------------------------------------------------
// Inputs 

void mousePressed() {
  // depending on situation we store mouse-position as "from" or as "to" point 
  if (situation==0) 
    from.add (new PVector (mouseX, mouseY));
  else if (situation==1)
    to.add (new PVector (mouseX, mouseY));

  // manage situation variable  
  situation++;
  if (situation>1)
    situation=0;
}//func

// -------------------------------------------------------------------------
// Tools 

void linePV ( PVector pv1, PVector pv2) {
  line(pv1.x, pv1.y, 
    pv2.x, pv2.y);
}//func 
//

Thank you Chrisir for your welcome and the prompt response. I was previously thinking of using PGraphics to store the last known image and paint over it with the new shape. But this is a great idea to use an ArrayList as it provides a way to create an Undo feature too - It does seem difficult as I need to distinguish the shape being drawn currently from all that has been drawn previously. Let me think thru the solution.

That’s not difficult.

Just draw the content of the ArrayList and on top the current moving ellipse.


An idea

What is more difficult:

to work with 2 parallel ArrayLists is cumbersome and error-prone. Instead you could make a class Ellipse with x,y and width and height and then let the ArrayList contain objects of that type:

ArrayList<Ellipse> ellipses = new ArrayList();

Then you can just store the data in the object of the class.

ellipses.add(new Ellipse(x,y,w,h));

There is a tutorial about objects. Please read it. https://www.processing.org/tutorials/objects/

Chrisir

Thank you Chrisir once again. I have tried implementing the same here: https://editor.p5js.org/rajmb502/sketches/0UB2fQ9t-

But I think I am making some mistake as the ellipse does not stop drawing even when mouse is released. Trying to debug now.

in mousePressed say hold=true;

in mouseReleased say hold =false;

in mouseDragged run its content only when if (hold==true)

before setup say boolean hold=false;

Thanks Chrisir. It is done and is working fine now :slight_smile:

Pasting code here for other’s reference:

let penColor = '#ffffff';
let bgColor = '#000000';
let strokeWt = 4;
let globalX = 0;
let globalY = 0;
let arrayOfShapes = [];
let data;
let shape = 'ellipse';
let hold = false;
  
function setup() {
  createCanvas(400, 400);
  stroke(penColor);
  strokeWeight(strokeWt);
  noFill();
  frameRate(10);
}

function draw() {
  background(color(bgColor));
  if (arrayOfShapes != null) {
    for(let i = 0; i < arrayOfShapes.length;i++) {
          if (arrayOfShapes[i].tool == 'ellipse') { 
            ellipseMode(CORNERS); 
            ellipse(arrayOfShapes[i].x, arrayOfShapes[i].y, arrayOfShapes[i].px, arrayOfShapes[i].py); 
          }
    }
  }
}


function mouseReleased() {
  data = {x:globalX, y:globalY, px:pmouseX, py:pmouseY, foregroundColor:penColor.toString('#rrggbb'), sW:strokeWt, tool:shape};
  arrayOfShapes.push(data);
  hold = false;
}

function mouseDragged() {
  if (hold == true) {
    push();
    strokeWeight(strokeWt/2);
    ellipseMode(CORNERS);
    ellipse(globalX, globalY, mouseX, mouseY);
    pop();    
  }
}

function mousePressed() {
  if ((mouseX > 0) && (mouseY > 0)) {
    globalX = mouseX; 
    globalY = mouseY;    
    hold = true;
  }
}
1 Like