So I got my code to work this more of a question in regards to Java and why it worked the way I got it VS. why it didn't work the first way I wrote it. This is the original code I wrote.
private void renderGUIExtraLives (SpriteBatch batch){
float x = GUIcamera.viewportWidth - 50 - Constants.LIVES_START * 50;
float y = -15;
for (int i = 0; i < Constants.LIVES_START; i++) {
if (worldController.lives <= i) {
batch.setColor(0.5f, 0.5f, 0.5f, 0.5f);
batch.draw(Assets.instance.bunny.head, x + i * 50, y, 50, 50, 120, 100, 0.35f, -0.35f, 0);
batch.setColor(1, 1, 1, 1);
}
}
}
This didn't work, it threw no errors, but it did not draw the lives to the screen, all I did was remove the curly braces of the if statement like so:
private void renderGUIExtraLives (SpriteBatch batch){
float x = GUIcamera.viewportWidth - 50 - Constants.LIVES_START * 50;
float y = -15;
for (int i = 0; i < Constants.LIVES_START; i++) {
if (worldController.lives <= i)
batch.setColor(0.5f, 0.5f, 0.5f, 0.5f);
batch.draw(Assets.instance.bunny.head, x + i * 50, y, 50, 50, 120, 100, 0.35f, -0.35f, 0);
batch.setColor(1, 1, 1, 1);
}
}
And now magically it worked, can some explain why it worked after I removed the curly braces from the nested if statement? I would really appreciate it, as well as any discussion regarding this topic would be great to read if someone has a link to a similar question or answer here on Stack.
Your indentation is wrong and misleading. If you remove the braces after the if only the next statement is conditionally executed. That means the code basically is:
for (int i = 0; i < Constants.LIVES_START; i++) {
if (worldController.lives <= i)
batch.setColor(0.5f, 0.5f, 0.5f, 0.5f);
batch.draw(Assets.instance.bunny.head, x + i * 50, y, 50, 50, 120, 100, 0.35f, -0.35f, 0);
batch.setColor(1, 1, 1, 1);
}
Should be clear why it "magically works" now, it just draws unconditionally.
Related
I'm prototyping a script to plot equally spaced points around a rotating plane and Processing is producing unexpected results?
This is my code:
int WHITE = 255;
int BLACK = 0;
void setup() {
size(500, 500);
}
void draw() {
background(WHITE);
translate(width/2, height/2); // move origin to center of window
// center ellipse
noStroke();
fill(255, 0, 0);
ellipse(0, 0, 10, 10); // center point, red
// satellite ellipses
fill(BLACK);
int points = 4;
for (int i = 0; i < points; i++) {
rotate(i * (TWO_PI / points));
ellipse(0, 100, 10, 10); // after plane rotation, plot point of size(10, 10), 100 points above y axis
}
}
When points = 4 I get the output I would expect, but when points = 5 // also when points = 3 or > 4, I get an output that is missing plotted points but still spaced correctly.
Why is this happening?
You're rotating too much: you don't want to rotate by i * angle at every iteration, because if we do we end up rotating so much that points end up overlapping. For example, with the code as is, with 3 points we want to place them at 0, 120, and 240 degrees (or, 120, 240, 360). But that's not what happens:
when i=0 we rotate by 0 degrees. So far so good.
when i=1 we rotate by 120 degrees on top of 0. Still good.
when i=2 we rotate by 240 degrees on top of 120. That's 120 degrees too far!
That's clearly not what we want, so just rotate by the fixed angle TAU / points and things'll work as expected:
for (int i = 0; i < points; i++) {
rotate(TAU / points);
ellipse(0, 100, 10, 10);
}
Alternatively, keep the incrementing angle, but then place the points without using rotate(), by using trigonometry to compute the placement:
float x = 0, y = 100, nx, ny, angle;
for (int i = 0; i < points; i++) {
angle = i * TAU / points;
nx = x * cos(a) - y * sin(a);
ny = x * sin(a) + y * cos(a);
ellipse(nx, ny, 10, 10);
}
I'm currently working on a project for school and am following a video tutorial, I'm pretty new to coding. From what I can tell everything looks right but when I run the preview it sends me a blank window with the error "ArrayIndexOutOfBoundsException:2"
PShape baseMap;
String csv[];
String myData[][];
//Setup BaseMap and csv info
void setup() {
size(1800, 900);
noLoop();
baseMap = loadShape("WorldMap.svg");
csv = loadStrings("FlightCancellations.csv");
myData = new String[csv.length][4];
for(int i=0; i<csv.length; i++) {
myData[i] = csv[i].split(",");
}
}
//draw
void draw() {
shape(baseMap, 0, 0, width, height);
noStroke();
fill(255, 0, 0, 50);
for(int i=0; i<myData.length; i++){
float graphLong = map(float(myData[i][2]), -180, 180, 0, width);
float graphLat = map(float(myData[i][3]), -90, 90, 0, height);
println(graphLong + " / " + graphLat);
ellipse(graphLong, graphLat, 10, 10);
}
}
Also, the mapped image works fine until I add
for(int i=0; i<myData.length; i++){
float graphLong = map(float(myData[i][2]), -180, 180, 0, width);
float graphLat = map(float(myData[i][3]), -90, 90, 0, height);
println(graphLong + " / " + graphLat);
You should get in the habit of checking that data exists before you use it in your program:
for(int i=0; i<myData.length; i++) {
if (myData[i].length > 3) { // Check that the array has at least 4 entries
float graphLong = map(float(myData[i][2]), -180, 180, 0, width);
float graphLat = map(float(myData[i][3]), -90, 90, 0, height);
println(graphLong + " / " + graphLat);
ellipse(graphLong, graphLat, 10, 10);
}
}
I tried to implement the following processing code in a JFrame in netbeans.
code was taken from http://www.geekmomprojects.com/mpu-6050-dmp-data-from-i2cdevlib/
However after running MyFrame.java the it only shows a still image in the frame.
It is not taking the serial readings.
MyProcessingSketch.java
`package testprocessing;
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
import java.util.Arrays;
import processing.core.*;
import processing.serial.*;
import processing.opengl.*;
import toxi.geom.*;
import toxi.processing.*;
public class MyProcessingSketch extends PApplet {
ToxiclibsSupport gfx;
Serial port; // The serial port
char[] teapotPacket = new char[14]; // InvenSense Teapot packet
int serialCount = 0; // current packet byte position
int synced = 0;
int interval = 0;
float[] q = new float[4];
Quaternion quat = new Quaternion(1, 0, 0, 0);
float[] gravity = new float[3];
float[] euler = new float[3];
float[] ypr = new float[3];
#Override
public void setup() {
// 300px square viewport using OpenGL rendering
size(300, 300, OPENGL);
gfx = new ToxiclibsSupport(this);
// setup lights and antialiasing
lights();
smooth();
// display serial port list for debugging/clarity
System.out.println(Arrays.toString(Serial.list()));
// get the first available port (use EITHER this OR the specific port code below)
//String portName = Serial.list()[0];
// get a specific serial port (use EITHER this OR the first-available code above)
String portName = "COM10";
// open the serial port
port = new Serial(this, portName, 9600);
// send single character to trigger DMP init/start
// (expected by MPU6050_DMP6 example Arduino sketch)
port.write('r');
}
#Override
public void draw() {
if (millis() - interval > 1000) {
// resend single character to trigger DMP init/start
// in case the MPU is halted/reset while applet is running
port.write('r');
interval = millis();
}
// black background
background(0);
// translate everything to the middle of the viewport
pushMatrix();
translate(width / 2, height / 2);
// 3-step rotation from yaw/pitch/roll angles (gimbal lock!)
// ...and other weirdness I haven't figured out yet
//rotateY(-ypr[0]);
//rotateZ(-ypr[1]);
//rotateX(-ypr[2]);
// toxiclibs direct angle/axis rotation from quaternion (NO gimbal lock!)
// (axis order [1, 3, 2] and inversion [-1, +1, +1] is a consequence of
// different coordinate system orientation assumptions between Processing
// and InvenSense DMP)
float[] axis = quat.toAxisAngle();
rotate(axis[0], -axis[1], axis[3], axis[2]);
// draw main body in red
fill(255, 0, 0, 200);
box(10, 10, 200);
// draw front-facing tip in blue
fill(0, 0, 255, 200);
pushMatrix();
translate(0, 0, -120);
rotateX(PI/2);
drawCylinder(0, 20, 20, 8);
popMatrix();
// draw wings and tail fin in green
fill(0, 255, 0, 200);
beginShape(TRIANGLES);
vertex(-100, 2, 30); vertex(0, 2, -80); vertex(100, 2, 30); // wing top layer
vertex(-100, -2, 30); vertex(0, -2, -80); vertex(100, -2, 30); // wing bottom layer
vertex(-2, 0, 98); vertex(-2, -30, 98); vertex(-2, 0, 70); // tail left layer
vertex( 2, 0, 98); vertex( 2, -30, 98); vertex( 2, 0, 70); // tail right layer
endShape();
beginShape(QUADS);
vertex(-100, 2, 30); vertex(-100, -2, 30); vertex( 0, -2, -80); vertex( 0, 2, -80);
vertex( 100, 2, 30); vertex( 100, -2, 30); vertex( 0, -2, -80); vertex( 0, 2, -80);
vertex(-100, 2, 30); vertex(-100, -2, 30); vertex(100, -2, 30); vertex(100, 2, 30);
vertex(-2, 0, 98); vertex(2, 0, 98); vertex(2, -30, 98); vertex(-2, -30, 98);
vertex(-2, 0, 98); vertex(2, 0, 98); vertex(2, 0, 70); vertex(-2, 0, 70);
vertex(-2, -30, 98); vertex(2, -30, 98); vertex(2, 0, 70); vertex(-2, 0, 70);
endShape();
popMatrix();
}
void serialEvent(Serial port) {
interval = millis();
while (port.available() > 0) {
int ch = port.read();
if (synced == 0 && ch != '$') return; // initial synchronization - also used to resync/realign if needed
synced = 1;
print ((char)ch);
if ((serialCount == 1 && ch != 2)
|| (serialCount == 12 && ch != '\r')
|| (serialCount == 13 && ch != '\n')) {
serialCount = 0;
synced = 0;
return;
}
if (serialCount > 0 || ch == '$') {
teapotPacket[serialCount++] = (char)ch;
if (serialCount == 14) {
serialCount = 0; // restart packet byte position
// get quaternion from data packet
q[0] = ((teapotPacket[2] << 8) | teapotPacket[3]) / 16384.0f;
q[1] = ((teapotPacket[4] << 8) | teapotPacket[5]) / 16384.0f;
q[2] = ((teapotPacket[6] << 8) | teapotPacket[7]) / 16384.0f;
q[3] = ((teapotPacket[8] << 8) | teapotPacket[9]) / 16384.0f;
for (int i = 0; i < 4; i++) if (q[i] >= 2) q[i] = -4 + q[i];
// set our toxilibs quaternion to new data
quat.set(q[0], q[1], q[2], q[3]);
/*
// below calculations unnecessary for orientation only using toxilibs
// calculate gravity vector
gravity[0] = 2 * (q[1]*q[3] - q[0]*q[2]);
gravity[1] = 2 * (q[0]*q[1] + q[2]*q[3]);
gravity[2] = q[0]*q[0] - q[1]*q[1] - q[2]*q[2] + q[3]*q[3];
// calculate Euler angles
euler[0] = atan2(2*q[1]*q[2] - 2*q[0]*q[3], 2*q[0]*q[0] + 2*q[1]*q[1] - 1);
euler[1] = -asin(2*q[1]*q[3] + 2*q[0]*q[2]);
euler[2] = atan2(2*q[2]*q[3] - 2*q[0]*q[1], 2*q[0]*q[0] + 2*q[3]*q[3] - 1);
// calculate yaw/pitch/roll angles
ypr[0] = atan2(2*q[1]*q[2] - 2*q[0]*q[3], 2*q[0]*q[0] + 2*q[1]*q[1] - 1);
ypr[1] = atan(gravity[0] / sqrt(gravity[1]*gravity[1] + gravity[2]*gravity[2]));
ypr[2] = atan(gravity[1] / sqrt(gravity[0]*gravity[0] + gravity[2]*gravity[2]));
// output various components for debugging
//println("q:\t" + round(q[0]*100.0f)/100.0f + "\t" + round(q[1]*100.0f)/100.0f + "\t" + round(q[2]*100.0f)/100.0f + "\t" + round(q[3]*100.0f)/100.0f);
//println("euler:\t" + euler[0]*180.0f/PI + "\t" + euler[1]*180.0f/PI + "\t" + euler[2]*180.0f/PI);
//println("ypr:\t" + ypr[0]*180.0f/PI + "\t" + ypr[1]*180.0f/PI + "\t" + ypr[2]*180.0f/PI);
*/
}
}
}
}
void drawCylinder(float topRadius, float bottomRadius, float tall, int sides) {
float angle = 0;
float angleIncrement = TWO_PI / sides;
beginShape(QUAD_STRIP);
for (int i = 0; i < sides + 1; ++i) {
vertex(topRadius*cos(angle), 0, topRadius*sin(angle));
vertex(bottomRadius*cos(angle), tall, bottomRadius*sin(angle));
angle += angleIncrement;
}
endShape();
// If it is not a cone, draw the circular top cap
if (topRadius != 0) {
angle = 0;
beginShape(TRIANGLE_FAN);
// Center point
vertex(0, 0, 0);
for (int i = 0; i < sides + 1; i++) {
vertex(topRadius * cos(angle), 0, topRadius * sin(angle));
angle += angleIncrement;
}
endShape();
}
// If it is not a cone, draw the circular bottom cap
if (bottomRadius != 0) {
angle = 0;
beginShape(TRIANGLE_FAN);
// Center point
vertex(0, tall, 0);
for (int i = 0; i < sides + 1; i++) {
vertex(bottomRadius * cos(angle), tall, bottomRadius * sin(angle));
angle += angleIncrement;
}
endShape();
}
}
public static void main(String[] args) {
PApplet.main(new String[] { "--present", "MyProcessingSketch" });
}
}
`
MyFrame.java
package testprocessing;
import javax.swing.JFrame;
public class MyFrame extends JFrame{
private MyProcessingSketch mysketch;
public MyFrame() {
setTitle("IMU");
setDefaultCloseOperation(EXIT_ON_CLOSE);
mysketch = new MyProcessingSketch();
mysketch.init();
add(mysketch);
}
public static void main(String[] args) {
MyFrame frame = new MyFrame();
frame.pack();
frame.setVisible(true);
}
}
So I have this little program (or sketch if you like). My problem is, and how should I word this, that the lines, when they are moving, kind of jitter and flutter a bit. Is this to do with the processing power of my computer, or should I code this in a different manner?
This is the code:
int i, j;
void setup() {
size(1440, 900);
background(0);
smooth();
strokeWeight(10);
i = width/2 - (width/2);
j = width;
}
void draw() {
fill(0, 10); // semi-transparent black
stroke(0);
rect(0, 0, width, height); //legger seg lag på lag
if (i < width-200) {
i+=4;
j-=4;
}
else {
i = width/2 - (width/2);
j = width;
}
stroke(255);
line(width/2, height, i, 30);
line(width/2, height, i+40, 30);
line(width/2, height, i+80, 30);
line(width/2, height, i+120, 30);
line(width/2, height, i+160, 30);
line(width/2, height, i+200, 30);
line(width/2, height, j, 30);
line(width/2, height, j-40, 30);
line(width/2, height, j-80, 30);
line(width/2, height, j-120, 30);
line(width/2, height, j-160, 30);
line(width/2, height, j-200, 30);
}
After doing a quick profile with JVisualVM it turns out that there are two culprits:
Rendering lines
Handling anti-aliased transparency
Rending lines:
Behind the scenes Processing is rendering each line as a shape(beginShape()/endShape()), in this case using LINES. You can give Processing a hand, and rather than using multiple beginShape/endShape calls(1 per line), just use one for all your lines:
beginShape(LINES);
for(int k = 0; k < 200; k+= 40){
vertex(hw, height);vertex(i+k, 30);
vertex(hw, height);vertex(j-k, 30);
}
endShape();
Anti-alias and transparency
Using transparency is generally computationally expensive, especially for large images.
Running the snippet bellow, press the mouse button and see how frameRate changes when transparency isn't used.
Anti-aliasing is also computationally expensive. Not as much as transparency, but in addition too, it makes a difference. Press any key to toggle between aliased and anti-aliased graphics
Here are a few tweaks to your code:
int i, j;
int hw;
boolean smooth;
void setup() {
size(1440, 900);
background(0);
strokeWeight(10);
hw = width/2;
i = width/2 - (width/2);//isn't this 0 ?
j = width;
}
void draw() {
fill(0,mousePressed ? 255 : 10); // semi-transparent black
noStroke();
rect(0, 0, width, height); //legger seg lag på lag
if (i < width-200) {
i+=4;
j-=4;
}
else {
i = 0;
j = width;
}
stroke(255);
beginShape(LINES);
for(int k = 0; k < 200; k+= 40){
vertex(hw, height);vertex(i+k, 30);
vertex(hw, height);vertex(j-k, 30);
}
endShape();
frame.setTitle((int)frameRate+" fps, smooth: " + smooth);
}
void keyReleased(){
smooth = !smooth;
if(smooth) smooth();
else noSmooth();
}
I am writing a program in Processing to make a 3-D scatterplot that one can rotate around in space using PeasyCam. Data is read in from a text file to an ArrayList of PVectors. The entire code is shown below. What I don't understand is that importTextfile() needs to be called (repeatedly) within draw() and this significantly slows things down. Why can't I get away with calling it once within setup()? With "println(pointsList)" within draw() I can see that pointList changes if it's not preceded by importTextFile(). Can anyone explain why?
(Update: From trying to construct a minimum working example I see now that the problem is when I make a PVector V and then write over it to map it to the display window. I would still appreciate feedback on a good work around that involves calling importTextFile() just during setup(). What do I use instead of .get() so I get a copy of what's in the Arraylist and not a pointer to the actually value in the ArrayList?)
Here is the code:
import peasy.*;
PeasyCam cam;
ArrayList <PVector>pointList;
int maxX = 0;
int maxY = 0;
int maxZ = 0;
int minX = 10000;
int minY = 10000;
int minZ = 10000;
int range = 0;
void setup() {
size(500, 500, P3D);
cam = new PeasyCam(this, width/2, width/2, width/2, 800);
cam.setMinimumDistance(100);
cam.setMaximumDistance(2000);
pointList = new ArrayList();
importTextFile();
println(pointList);
//Determine min and max along each axis
for (int i=0; i < pointList.size(); i++) {
PVector R = pointList.get(i);
if (R.x > maxX) {
maxX = (int)R.x;
}
if (R.x < minX) {
minX = (int)R.x;
}
if (R.y > maxY) {
maxY = (int)R.y;
}
if (R.y < minY) {
minY = (int)R.y;
}
if (R.z > maxZ) {
maxZ = (int)R.z;
}
if (R.z < minZ) {
minZ = (int)R.z;
}
}
if (maxX - minX > range) {
range = maxX - minX;
}
if (maxY - minY > range) {
range = maxY - minY;
}
if (maxZ - minZ > range) {
range = maxZ - minZ;
}
println(pointList);
}
void draw() {
//importTextFile(); Uncomment to make run properly
println(pointList);
background(255);
stroke(0);
strokeWeight(2);
line(0, 0, 0, width, 0, 0);
line(0, 0, 0, 0, width, 0);
line(0, 0, 0, 0, 0, width);
stroke(150);
strokeWeight(1);
for (int i=1; i<6; i++) {
line(i*width/5, 0, 0, i*width/5, width, 0);
line(0, i*width/5, 0, width, i*width/5, 0);
}
lights();
noStroke();
fill(255, 0, 0);
sphereDetail(10);
//**The problem is here**
for (int i=0; i < pointList.size(); i++) {
PVector V = pointList.get(i);
V.x = map(V.x, minX-50, minX+range+50, 0, width);
V.y = map(V.y, minY-50, minY+range+50, 0, width);
V.z = map(V.z, minZ-50, minZ+range+50, 0, width);
pushMatrix();
translate(V.x, V.y, V.z);
sphere(4);
popMatrix();
}
}
void importTextFile() {
String[] strLines = loadStrings("positions.txt");
for (int i = 0; i < strLines.length; ++i) {
String[] arrTokens = split(strLines[i], ',');
float xx = float(arrTokens[2]);
float yy = float(arrTokens[1]);
float zz = float(arrTokens[0]);
pointList.add( new PVector(xx,zz,yy) );
}
}
You could continue to refactor, I don't know why they show this way of iterating through ArrayLists in the Processing documentation but here is a go at refactoring it:
float calcV (float n) {
return map (n, minX-50, minX+range+50, 0, width);
}
for (PVector V: pointList) {
pushMatrix();
translate(calcV(V.x), calcV(V.y), calcV(V.z));
sphere(4);
popMatrix();
}
You might consider creating a new Class with a PVector as a field or extending PVector. Daniel Shiffman uses this pattern a lot. Create a Class with a PVector for position, another for velocity, a method to calc the new position per frame and another to draw it out to screen. That gives you a lot of flexibility without having to write a ton of loops, other than one to calc and display the objects in your ArrayList.
As PVector is an object, when you do PVector V = pointList.get(i) you pass the reference for that specific element in pointList, to V. It's address in memory. So now V and pointList.get(number) share the same memory address. Any change in either one will change both, as they are two different pointers to same place.
But, if you do:
PVector V = new PVector(pointList.get(i).x, pointList.get(i).y, pointList.get(i).z);
V will be a new object and things will work as you want, cause now V has it's own memory address. And poinList will remain untouched. Same thing goes for other objects, arrays for instance, try this to see:
int[] one = new int[3];
int[] two = new int[3];
void setup(){
one[0] = 0;
one[1] = 1;
one[2] = 2;
print("one firstPrint -> \n");
println(one);
two = one;
print("two firstPrint -> \n");
println(two);
two[2] = 4;
// we didn't mean to change one, but...
print("one secondPrint -> \n");
println(one);
}
I fixed things with the code below but am still interested in other ways.
for (int i=0; i < pointList.size(); i++) {
PVector V = pointList.get(i);
PVector W = new PVector(0.0, 0.0, 0.0);
W.x = map(V.x, minX-50, minX+range+50, 0, width);
W.y = map(V.y, minY-50, minY+range+50, 0, width);
W.z = map(V.z, minZ-50, minZ+range+50, 0, width);
pushMatrix();
translate(W.x, W.y, W.z);
sphere(4);
popMatrix();
}