Press 'r' key, then click tom move to another location. Select angle by clicking on circle (or hold 'a' key); Press 'i' key and set velocity (or hold 'v' key)
xxxxxxxxxx
//// Projectile_1: angle-select via circle; velocity & angle determined by key duration.
//// Angle selector. (Also use key duration to select values.)
//// Click to locate selector at (x,y)
String title="Projectile #1";
String author="bam@professorBAM.com";
String version= "9817a";
String sub1="Click near pointer to change angle";
String sub2="\n(then click elsewhere.)";
String subtitle=sub1+sub2;
String news="";
String helpText="Press 'r' key to relocate the angle-selector";
String blank=" ", tab=" ", nl="\n", pi="π";
color bgcolor= color(200,225,250);
int nextX=10, nextY=100, nextH=15, nextCounter=0;
int fireX=width,fireY=height;
//
int durationX=300, durationY=470, durationDX=2, durationDY=12;
float rate=60;
int n=0;
int press=0, release=0, duration=0;
char whatkey=' ';
float velocity=50, angle=PI/4, gravity=9.81;
float vx,vy;
float previous[];
int np=0;
// Circle to specify angle.
float ax=50, ay=500, ar=50; // Initial position
Angle a= new Angle( ax, ay, ar );
boolean relocate=false, initial=false, pause=false;
Bullet b= new Bullet( ax,ay, 0, 0 );
void setup() {
size(800,600);
frameRate(rate);
//-- helpText= getHelp();
reset();
previous= new float[50];
np=0;
for (float p : previous) {p=0;}
previous[0]=50;
previous[1]=200;
previous[2]=200;
previous[3]=300;
previous[4]=400;
previous[5]=500;
previous[6]=600;
previous[7]=700;
}
void reset() {
vx= velocity * (float) Math.cos(a.angle);
vy= velocity * (float) Math.sin(a.angle);
if (b.distance > 0 && np<previous.length) {
println( "SAVING ", b.distance );
previous[np++]= b.distance;
}
b= new Bullet( a.cx,a.cy, vx,vy );
}
void draw() {
background( bgcolor );
fill(0);
textSize(24);
text( title, width/3, 20 );
textSize(12);
text( subtitle, width/3, 40 );
fill(255,0,255);
text( news+".", 10, 66 );
fill(0,100,0);
showHelp( width*2/3, 66 );
fill(100);
textSize(10);
text( author, 10, height-20 );
fill(150);
text( "©2019 "+version, 10, height-10 );
fill(0,0,255);
data( 10, 110 );
a.show();
//// Projectile in flight.
b.show();
if (! pause) b.move();
if (duration>0) text( duration, durationX+25, durationY );
}
void data( int x, int y ) {
nextX=x;
nextY=y;
nextCounter=0;
next( "Velocity = " +velocity );
next( "Angle = " +a.angle);
next( " = " +a.pimul+ " π" );
next( " ~ " +a.degrees+ "º" );
//
next( "" );
next( "position: (" +(int)b.x+ ", " +(int)b.y+ ")" );
next( "velocity: " +(int)b.dx+ ", " +(int)b.dy );
next( "time: " +(int)(1000*b.time)+ " ms");
// Display old markers.
fill( 150,0,150, 50 );
if (previous[0]>0) {
for (int j=0; j<previous.length; ++j) {
if (previous[j]>0) {
fill(150,0,150 );
b.oldmarker( (int) previous[j] );
};
}
}
}
void next( String s ) {
text( s, nextX, nextY + nextH*nextCounter++ ); // Next line of text
}
void nextReset() {
nextCounter=0;
}
void showHelp( int x, int y ) {
nextX=x;
nextY=y;
nextCounter=0;
next( "Press 'r' key to relocate selector" );
next( "Press 'i' key to specify initial velocity" );
next( " Holding 'a' key also sets angle" );
next( " Holding 'v' key also sets velocity" );
next( " Holding 'p' key pauses the action" );
next( "q to quit" );
fill(255,0,0);
next( "Press 'f' key to FIRE the projectile\n (or click HERE)" );
//Save coordinaters of "FIRE"
fireX= nextX+112;
fireY= nextY+8 + nextH*nextCounter++;
fill( 255,220,220, 150 );
ellipse( fireX, fireY, 50,50 );
}
void keyPressed() {
if (key == 'p') { pause=true; return; }
pause=false;
if (press==0) {
news= ""+key;
press= millis(); // Save the starting time for keypress,
durationX=300;
whatkey=key;
text( ""+whatkey, durationX, durationY );
news=""+whatkey;
}
news+= ""+whatkey;
point( durationX+20, durationY ); // Keep adding dots, while key remains pressed.
durationX+= durationDX;
++n;
}
void keyReleased() {
if (key == CODED) { news=""; return; } // Ignore control keys.
if (key == ' ') { news=""; return; }
if (key == 'p') { pause=false; return; }
if (key == 'q') { exit(); }
release= millis();
duration= release - press;
news= whatkey+ ": " +duration+ " ms.";
//-- news+= "\n (" +press+ "-" +release+ ")";
if (n>1) news+= " " +n+ " frames";
press=release=0;
n=0;
//
if (key == 'f') { fire(); }
else if (key == 'a') angle= (duration/100) * PI * 2;
else if (key == 'v') velocity= duration / 100;
else if (key == 'g') gravity= duration / 100;
else if (key=='+' || key=='=') a.plus();
else if (key=='-' || key=='_') a.minus();
//
else if (key == 'r') {
// Relocate the selector
subtitle= "Click to relocate the selector";
news= "Click to relocate the selector";
relocate=true;
a.visible=false;
}
else if (key == 'i') {
// Initial velocity
subtitle= "Click along vector line to indicate initial velocity";
news= "Click to along vector line to indicate initial velocity";
initial=true;
a.vector=true;
}
else if (key == 's') { b.stop(); return; }
else if (key == 'r') {
// Relocate the selector
relocate=true;
a.visible=false;
return;
}
else if (key == 'i') {
// Indicate the velocity (along vector line)
initial=true;
a.vector=true;
return;
}
//
else news+= "\nNO SUCH KEY: " +whatkey;
}
void fire() {
background(255,0,0);
reset();
b.time=0;
}
void mousePressed() {
pause=false;
if ( a.near(mouseX,mouseY) ) {
// click is within circle
a.select(mouseX,mouseY);
return;
}
if ( dist(mouseX,mouseY, fireX,fireY) < 30) {
// click is near FIRE
fire();
return;
}
else if (relocate) {
ax=mouseX; // New location for selector
ay=mouseY;
float tmp= a.angle;
a= new Angle( ax, ay, ar );
a.newAngle( tmp );
a.visible=true;
relocate=false;
subtitle=sub1+sub2;
}
else if (initial) {
ax=mouseX; // New location for selector
ay=mouseY;
velocity= dist( a.cx,a.cy, mouseX, mouseY ) / 3;
a.vector=false;
initial=false;
news= "initial velocity: " + velocity;
subtitle=sub1+sub2;
}
else {
a.visible=false;
}
}
class Angle {
float angle=PI/4, pimul=0.25, degrees=45;
float cx=50, cy=400, cr=40; // Circle
float px=50, py=400; // Pointer
int crust=3, thick=12;
boolean visible=true, vector=false;
//// CONSTRUCTOR(S) ////
Angle(float cx, float cy, float cr) {
this.cx=cx;
this.cy=cy;
this.cr=cr;
}
//// METHODS ////
void show() {
if (visible) {
circle();
fill(0);
text( "Click on circle\nto change angle", cx-cr+6, cy-cr-36);
}
pointer(angle);
}
void circle( ) {
// Draw a circle to specify angle.
strokeWeight(crust);
fill( 200,255,255 );
stroke(0,0,255);
ellipse( cx, cy, cr*2, cr*2 );
strokeWeight(1);
line( cx,cy, cx+cr, cy );
line( cx,cy, cx-cr, cy );
line( cx,cy, cx, cy+cr );
line( cx,cy, cx, cy-cr );
}
void pointer( float angle ) {
float px,py;
px= cx + cr*cos(angle);
py= cy - cr*sin(angle);
strokeWeight(thick);
stroke(0);
line( cx,cy, px, py );
fill(255);
noStroke();
strokeWeight(1);
int tip= int(thick*3/4); // white circle at tip
ellipse( px,py, tip,tip );
fill(0);
if (visible) text( degrees+ "º", px+20,py );
text( degrees+ "º", cx-20,cy+cr+20 );
// Horizon.
stroke( 0,150,0, 100 );
line( cx, cy, width-20, cy );
// Vector line
if (vector) {
float vx,vy;
vx= cx + width*cos(angle);
vy= cy - width*sin(angle);
stroke( 255,0,0 );
fill( 255,0,0 );
line( cx, cy, vx, vy );
}
fill(0);
}
boolean near( float x, float y ) {
return ( dist( x,y, cx,cy) < cr+20 );
// true if x,y is within (or near) circle
}
void select( float x, float y ) {
// Set new angle
newAngle( getAngle( x, y ) );
}
void newAngle( float radians ) {
angle= radians;
degrees= angle * 180 / PI;
pimul= (angle/PI)%2;
visible=true;
}
float getAngle( float x, float y ) {
// Returns angle above horizontal (-π/2 to 3π/2)
float dx, dy;
dx= x-cx;
dy= y-cy;
if (x>=cx) return -atan( dy / dx );
else return PI - atan( dy / dx );
}
// + or - bumps angle ny +/- 1 degree
void plus() { setAngle( degrees+1 ); }
void minus() { setAngle( degrees-1 ); }
void setAngle( float d ) {
degrees= (int) d;
angle= degrees * PI / 180;
pimul= (angle/PI)%2;
visible=true;
}
}
class Bullet {
float x=0,y=0,dx=0,dy=0; // Position & velocity components.
float angle= PI/4;
float time=0; // Time
float mass=1; // (???)
float gravity= 9.81;
float radius=10;
float horizon, distance=0;
//// CONSTRUCTOR(S) ////
Bullet( float x, float y, float dx, float dy ) {
this.x=x; this.y=y;
this.dx=dx; this.dy=dy;
horizon= y;
}
//// METHODS ////
void move() {
if (x>width || y>height+20) stop();
if (x<0) stop();
if (time<0) return;
++time;
x+= dx/rate;
y-= dy/rate;
dy-= gravity/rate;
if (y>horizon) {
news= "Bullet drops below horizon!";
}
if (y>height+20) {
news= "";
}
}
void stop() {
time= time>0 ? -time : time;
}
void show() {
if (time >= 0) {
fill(0);
if (y>horizon) {
fill( 20,20,20, 100 );
splash( x,horizon );
if (distance<=0) distance=x;
}
if (y<height) ellipse( x,y, 2*radius,2*radius );
}
// Show distance (if nonzero).
if (distance>0) { marker( distance); }
}
void splash( float xx, float yy ) {
line( xx,yy, xx-60,yy-40 );
line( xx,yy, xx-20,yy-60 );
line( xx,yy, xx+20,yy-60 );
line( xx,yy, xx+60,yy-40 );
}
void marker( float x ) {
fill(255,0,255);
rect(x-2,horizon-4, 4,8 );
text( x, x,horizon );
}
void oldmarker( float x ) {
fill(150,0,150, 50);
rect(x-2,horizon-4, 4,8 );
text( (int) x, x-2,horizon+14 );
}
}