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.
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?
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();
final float RADIUS_X = 150;
final float RADIUS_Y = 300;
// 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;
mouseStopX=mouseX-RADIUS_X*2;
mouseStartY=mouseY-RADIUS_Y;
}
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;
leftList.add(new PVector(newX-RADIUS_X, newY+mouseStartY));
// left arm ---
dist1=map(mouseX, mouseStartX, mouseStopX, TWO_PI-PI/2, PI-PI/2);
newX=cos(dist1) * RADIUS_X + mouseStartX;
newY=sin(dist1) * RADIUS_Y + height/2;
rightList.add(new PVector(newX-RADIUS_X, newY+mouseStartY));
}
//
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();
final float RADIUS_X = 300;
final float RADIUS_Y = 150;
// 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;
mouseStopX=mouseX-RADIUS_X*2;
mouseStartY=mouseY-RADIUS_Y;
}
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;
listLowerArm.add(new PVector(newX-RADIUS_X, newY+mouseStartY));
// upper arm ---
float angleUpperArm=map(mouseX, mouseStartX, mouseStopX, TWO_PI, PI);
angleUpperArm=constrain(angleUpperArm, PI, TWO_PI);
newX=cos(angleUpperArm) * RADIUS_X + mouseStartX;
newY=sin(angleUpperArm) * RADIUS_Y + height/2;
listUpperArm.add(new PVector(newX-RADIUS_X, newY+mouseStartY));
}
//
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;
}
public PVector nextXY(float mouseX, float startRadians, float stopRadians) {
float radians = map(mouseX, mouseStart.x, mouseStopX, startRadians, stopRadians);
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);
return topXY;
}