# Manually drawing an oval, in response to a straight mouse drag

I wrote a sketch for penning letters and digits on a touchscreen, and then manipulating them. I use it as a teaching tool for young kids. The penning part is trivial, just drawing lines in response to mouse events.

I need to add a feature where a single, roughly horizontal swipe, from right to left appears to pen a complete ‘0’ (zero). So, as I swipe, it would start at the 90-degree point, split to draw up toward 0 and down toward 180 (simultaneously) and then converge back down toward a single point, completing at 270. Ideally, it would be nice to add a little noise() so the end results appear finger-penned and slightly unique, but still smooth. (some samples below)

I need to draw the 0’s in different sizes, relative to other objects on the screen. The desired width and height are calculated in mouseDown() at the beginning of the swipe.

I have it running, gradually copying from saved image files of penned 0’s, but they blur too much when resize()'d, even if it’s just a small amount around the edges. Hence, Plan B, doing the drawing in real time.

Any help greatly appreciated. I’m super solid in code/processing, but always struggle with radians/trig/spacial stuff.

1 Like

drag mouse slowly from right to left

The center of the “0”:

• x : from mouse position when clicking
• y always the same: `height/2`

Chrisir

``````

// gather all points
ArrayList<PVector> list0=new ArrayList();

// this happens when you click and hold mouse
boolean hold=false;
float mouseStartX, mouseStartY;

void setup() {
size(800, 800);
}

void draw() {
// show entire list
noStroke();
for (PVector pv : list0) {
ellipse(pv.x, pv.y,
6, 6);
}//for

// During mouse drag:
if (hold) {
storeLeftAndRightArmInList();
}//if
}//draw()

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

void mousePressed() {
hold=true;
mouseStartX=mouseX;
mouseStartY=mouseY;
}

void mouseReleased () {
hold=false;
}

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

void storeLeftAndRightArmInList() {

// right arm ---
float dist1=map(dist(mouseX, 0, mouseStartX, 0),
0, width/2,
0, PI);

float dist2 = dist1-PI/2;
dist2=constrain(dist2, -PI/2, PI-PI/2);
float newX=cos(dist2) * RADIUS_X + mouseStartX;
float newY=sin(dist2) * RADIUS_Y + height/2;

// left arm ---
dist1=map(dist(mouseX, 0, mouseStartX, 0),
0, width/2,
TWO_PI-PI/2, PI-PI/2);

}
//
``````
1 Like

Chrisir! That is spectacular! I can’t thank you enough, and I’ve got to find a way to send you a small Starbuck’s card (or similar)!

I need to do some tweaks, and will give them a shot tomorrow. Mainly, I want to draw the oval from right to left, instead of top to bottom. I was picturing 90-degrees at 3:00, and 270 at 9:00, my mistake in the description. So the oval would begin and end matching the x-coordinate with the mouse. So basically changing left/right-Arms to top/bottom-Arms.

What a huge help though! Easiest way to drop you a cup of coffee?

Mike

2 Likes

No big deal.

I can rotate this tomorrow

0 degrees in processing is to the right (east) by the way

A couple of tweaks:
using line() instead of ellipse() (requires separate lists)
placing the right edge at mouseDown
finishing the left edge at (mouseDown - diameter)
clearing and drawing more than once

Still drawing top to bottom, can give that a try tomorrow afternoon.

Thanks again Chrisir! Would love to get you a coffee!

``````
// gather all points
ArrayList<PVector> leftList=new ArrayList();
ArrayList<PVector> rightList=new ArrayList();

// this happens when you click and hold mouse
boolean hold=false;
float mouseStartX, mouseStopX, mouseStartY;

void setup() {
size(800, 800);
smooth();
}

void draw() {
// show lists
for (int i=1; i<leftList.size(); i++) { // start at 1
line(leftList.get(i-1).x, leftList.get(i-1).y, leftList.get(i).x, leftList.get(i).y);
}//for

for (int i=1; i<rightList.size(); i++) { // start at 1
line(rightList.get(i-1).x, rightList.get(i-1).y, rightList.get(i).x, rightList.get(i).y);
}//for

// During mouse drag:
if (hold) {
storeLeftAndRightArmInList();
}//if
}//draw()

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

void mousePressed() {
// clear and start over
background(#CCCCCC);
leftList.clear();
rightList.clear();

hold=true;
mouseStartX=mouseX;
}

void mouseReleased () {
hold=false;
}

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

void storeLeftAndRightArmInList() {
// right arm ---
float dist1=map(mouseX, mouseStartX, mouseStopX, 0, PI);

float dist2 = dist1-PI/2;
dist2=constrain(dist2, -PI/2, PI-PI/2);
float newX=cos(dist2) * RADIUS_X + mouseStartX;
float newY=sin(dist2) * RADIUS_Y + height/2;

// left arm ---
dist1=map(mouseX, mouseStartX, mouseStopX, TWO_PI-PI/2, PI-PI/2);

}
//
``````
2 Likes

instead of using `line()` you could also store the old
angle and make a for-loop from old to new angle, adding all points in between to the list
(I haven’t done this).

Chrisir

``````

// gather all points
ArrayList<PVector> listLowerArm=new ArrayList();
ArrayList<PVector> listUpperArm=new ArrayList();

// this happens when you click and hold mouse
boolean hold=false;
float mouseStartX, mouseStopX, mouseStartY;

void setup() {
size(800, 800);
smooth();
}

void draw() {

background(#CCCCCC);

// show lists
for (int i=1; i<listLowerArm.size(); i++) { // start at 1
line(listLowerArm.get(i-1).x, listLowerArm.get(i-1).y,
listLowerArm.get(i).x, listLowerArm.get(i).y);
}//for

for (int i=1; i<listUpperArm.size(); i++) { // start at 1
line(listUpperArm.get(i-1).x, listUpperArm.get(i-1).y,
listUpperArm.get(i).x, listUpperArm.get(i).y);
}//for

// During mouse drag:
if (hold) {
storeArmsInList();
}//if
}//draw()

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

void mousePressed() {
// clear and start over
listLowerArm.clear();
listUpperArm.clear();

hold=true;
mouseStartX=mouseX;
}

void mouseReleased () {
hold=false;
}

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

void storeArmsInList() {
// lower arm ---
float angleLowerArm=map(mouseX, mouseStartX, mouseStopX, 0, PI);

// float dist2 = dist1-PI/2;
angleLowerArm=constrain(angleLowerArm, 0, PI);

float newX=cos(angleLowerArm) * RADIUS_X + mouseStartX;
float newY=sin(angleLowerArm) * RADIUS_Y + height/2;

// upper arm ---
float angleUpperArm=map(mouseX, mouseStartX, mouseStopX, TWO_PI, PI);
angleUpperArm=constrain(angleUpperArm, PI, TWO_PI);

}
//

``````
1 Like

here is a totally new version just allowing storage of any ellipse

``````// gather all data
ArrayList<EllipseData> list=new ArrayList();

// this happens when you click and hold mouse
boolean hold=false;
float mouseStartX, mouseStartY;

void setup() {
size(800, 800);
}

void draw() {
background(#CCCCCC);

// show lists
for (EllipseData ed : list) {
ed.display();
}//for

// During mouse drag:
if (hold) {
fill(255);
ellipseMode(CENTER);
ellipse(mouseStartX, mouseStartY,
(mouseX-mouseStartX)*2, (mouseY-mouseStartY)*2);
}//if
}//draw()

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

void mousePressed() {
hold=true;
mouseStartX=mouseX;
mouseStartY=mouseY;
}

void mouseReleased () {
hold=false;
EllipseData ed=new EllipseData ( new PVector(mouseStartX, mouseStartY),
new PVector( (mouseX-mouseStartX)*2, (mouseY-mouseStartY)*2) );
}

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

class EllipseData {

PVector posEll;
PVector sizeEll;
color colEll = color (random(255), random(255), random(255));

EllipseData(PVector p_,
PVector s_) {
posEll  = p_.copy();
sizeEll = s_.copy();
}

void display() {
ellipseMode(CENTER);
fill(colEll);
ellipse(posEll.x, posEll.y,
sizeEll.x, sizeEll.y ) ;
}
} //class
//
``````
1 Like

Thanks a million Chrisir! Just what I was looking for!

Here’s the version I reworked to fit into my original app. Some funky requirements (like only drawing each point once and drawing from mouseDragged) but got everything I needed thanks to you.

``````
PVector mouseStart, topXY, bottomXY;
float mouseStopX;

float zWidth = 150; // width of zero
float zHeight = 250; // height of zero

public void setup() {
size(800, 600);
smooth();
strokeWeight(3);
}

public void draw() {
// drawn inside extendZeroSwipe (triggered from mouseDragged)
}

public void mouseReleased() {
mouseStart = null; // flag to start over
}

public void mouseDragged() {
if (mouseStart == null)
mouseStart = startZeroSwipe();
extendZeroSwipe(max(mouseX, mouseStopX));
}

public void extendZeroSwipe(float x) {
PVector nextTop = nextXY(x, 2 * PI, PI);
PVector nextBottom = nextXY(x, 0, PI);
line(topXY.x, topXY.y, nextTop.x, nextTop.y);
line(bottomXY.x, bottomXY.y, nextBottom.x, nextBottom.y);
topXY = nextTop;
bottomXY = nextBottom;
}

float x = cos(radians) * zWidth + mouseStart.x;
float y = sin(radians) * zHeight + mouseStart.y;
return new PVector(x - zWidth, y);
}

public PVector startZeroSwipe() { // start/restart oval
background(204);
mouseStart = new PVector(mouseX, mouseY);
mouseStopX = mouseX - zWidth * 2;
topXY = nextXY(mouseStart.x, 2 * PI, PI);
bottomXY = nextXY(mouseStart.x, 0, PI);