We closed this forum 18 June 2010. It has served us well since 2005 as the ALPHA forum did before it from 2002 to 2005. New discussions are ongoing at the new URL http://forum.processing.org. You'll need to sign up and get a new user account. We're sorry about that inconvenience, but we think it's better in the long run. The content on this forum will remain online.
IndexProgramming Questions & HelpSyntax Questions › There must be a better way to code this.
Page Index Toggle Pages: 1
There must be a better way to code this. (Read 1955 times)
There must be a better way to code this.
Jun 24th, 2009, 9:37am
 
Firstly, is there a better way to write the below code to find which button is rolled over without having to use a For loop to go through an ArrayList, then trigger the update function? I'm talking about the code in draw().

It's a common problem I face while learning to program - where I have to use many For loops to cycle through Arrays or ArrayLists in order to check the status of objects, and this always slows my code down. It's causing me no end of frustration, there HAS to a better way of doing this, this is driving me crazy.

Secondly, this code doesn't work (only half of buttons roll over, and these cause neighbouring buttons also to roll over) and I think this is related to the first question.

The code below is much simplified version of an interface I'm trying to build, that I created to isolate these problems. So the below does not run slowly, but the more complex version (where multiple objects from different classes interrelate) does.

Any help would be greatly, greatly appreciated!!!!!

Code:

ArrayList buttons;

void setup() {
 size(400, 400);
 buttons = new ArrayList();
 int numXboxes = 5;
 int numYboxes = 5;
 int totalBoxes = numXboxes * numYboxes;
 int ux = width / numXboxes;
 int uy = height / numYboxes;
 int xcounter = 0;
 int ycounter = 0;

 for(int i = 0 ; i < totalBoxes; i++) {
   buttons.add(new Button((xcounter*ux), (ycounter*uy), ux, color(204), color(255), color(102), i));
   if (xcounter >= (numXboxes-1)) {
     xcounter=0;
     ycounter++;
   }
   else {
     xcounter++;
   }
 }
}

void draw() {
 background(0);
 for (int i=0; i< buttons.size(); i++) {
   Button temp = (Button) buttons.get(i);
   temp.display();
   temp.update();
 }
}

class Button {
 int x, y;
 int size;
 color baseGray;
 color overGray;
 color pressGray;
 boolean over = false;
 boolean pressed = false;
 int id;

 Button(int xp, int yp, int s, color b, color o, color p, int ident) {
   x = xp;
   y = yp;
   size = s;
   id = ident;
   baseGray = b;
   overGray = o;
   pressGray = p;
 }

 void update() {
   if ((mouseX >= x) && (mouseY <= x+size) &&
     (mouseY >= y) && (mouseY <= y+size)) {  
     over = true;
   }
   else {
     over = false;
   }
 }

 void display() {
   if (pressed == true) {
     fill(pressGray);
   }
   else if (over == true) {
     fill(overGray);
   }
   else {
     fill(baseGray);
   }
   stroke(255);
   rect(x, y, size, size);
 }
}
Re: There must be a better way to code this.
Reply #1 - Jun 24th, 2009, 10:07am
 
jbucks wrote on Jun 24th, 2009, 9:37am:
Secondly, this code doesn't work (only half of buttons roll over, and these cause neighbouring buttons also to roll over) and I think this is related to the first question.


I can at least answer this part of the question quickly:

void update() {
   if ((mouseX >= x) && (mouseY <= x+size) &&

Try changing that to mouseX Wink
Re: There must be a better way to code this.
Reply #2 - Jun 24th, 2009, 11:16am
 
First of all, ditto on blindfish's observation. You need to change that mouseY value to mouseX, and it'll fix your problem with neighboring boxes being affected.

As for your speed problem, you pretty much have to use for() loops to check the statuses of your objects. Otherwise, how else will they know what to do? Although you mentioned this example is super basic compared to what you are really trying to do, I don't see how running some for() loops could really slow down your sketch all that much, unless you are using thousands and thousands of objects and buttons and stuff. I feel like there may be something else in your code that's causing the slow down, perhaps in your graphics drawing.

If you want to not worry about it and want a quick band-aid, maybe you could run your rollover checking in an another thread. That way all your statuses are being updated in the background, while your main loop can work on the drawing routines. Just a thought.  
Re: There must be a better way to code this.
Reply #3 - Jun 24th, 2009, 12:02pm
 
Quote:
Firstly, is there a better way to write the below code to find which button is rolled over without having to use a For loop to go through an ArrayList, then trigger the update function? I'm talking about the code in draw().


There is always an alternative way but not always a better way. Ultimately someone must loop thorough all the buttons but Processing has some neat features to make your life easier. Unfortunately some of these can only be found on the developers site - I'll put a link in below.

One way to to get each button to respond to the mouse AND be responsible for drawing itself.

Anyway before I discuss this further here is your code which I have modified to take advantage of one of the features of Processing.

Notice draw() has no loops  Cheesy

Code:
ArrayList buttons;
Button overButton = null;
Button clickedButton = null;
Button b;
int counter = 0;

void setup() {
 size(400, 400);
 buttons = new ArrayList();
 int numXboxes = 5;
 int numYboxes = 5;
 int totalBoxes = numXboxes * numYboxes;
 int ux = width / numXboxes;
 int uy = height / numYboxes;
 int xcounter = 0;
 int ycounter = 0;

 for(int i = 0 ; i < totalBoxes; i++) {
   b = new Button((xcounter*ux), (ycounter*uy), ux, color(204), color(255), color(102), i);
   buttons.add(b);
   registerDraw(b);
   registerMouseEvent(b);

   if (xcounter >= (numXboxes-1)) {
     xcounter=0;
     ycounter++;
   }
   else {
     xcounter++;
   }
 }
}

void draw() {
 background(0);
 if(overButton != null)
   println("Mouse is over " + overButton.id);
 if(clickedButton != null)
   println("Mouse is clicked " + clickedButton.id);

 // These must be last 2 lines in draw method
 overButton = null;  
 clickedButton = null;
}

public class Button {
 int x, y;
 int size;
 color baseGray;
 color overGray;
 color pressGray;
 boolean over = false;
 boolean pressed = false;
 int id;

 Button(int xp, int yp, int s, color b, color o, color p, int ident) {
   x = xp;
   y = yp;
   size = s;
   id = ident;
   baseGray = b;
   overGray = o;
   pressGray = p;
 }

 void mouseEvent(MouseEvent event){
   if ((mouseX >= x) && (mouseX <= x+size) && (mouseY >= y) && (mouseY <= y+size)) {  
     over = true;
     overButton = this;
     switch(event.getID()){
     case MouseEvent.MOUSE_CLICKED:
       pressed = true;
       clickedButton = this;
       break;
     }
   }
   else {
     over = false;
     pressed = false;
   }
 }

 void draw() {
   if (pressed == true) {
     fill(pressGray);
   }
   else if (over == true) {
     fill(overGray);
   }
   else {
     fill(baseGray);
   }
   stroke(255);
   rect(x, y, size, size);
 }
}


Notice in setup the 2 lines
Code:
    b = new Button((xcounter*ux), (ycounter*uy), ux, color(204), color(255), color(102), i);
   buttons.add(b);
   registerDraw(b);
   registerMouseEvent(b);

used to register the button for drawing and mouse events.

So in the Button class the main difference here is that the class has been declared public - absolutely essential for this to work, and we have 2 methods to handle drawing and mouse events.

Since we have registered these events then the mouse evnet handler is called whenever the mouse is moved, pressed, clicked etc. and the draw method will be called every frame.

I promised a link for the info on the developers site here it is
http://dev.processing.org/libraries/basics.html

Hope this helps. Smiley
Re: There must be a better way to code this.
Reply #4 - Jun 24th, 2009, 12:10pm
 
OK, for the heck of it, I rewrote your above code to update the rollover status in a new thread. Seems to be working pretty well. If you implement this into your actual project, you should see some better results.

First of all, I wrote a Rollover class which handles all the rollover updating of your button objects. Here's the class:
Code:

public class Rollover implements Runnable {
 Thread thread;
 boolean runThread;
 
 public Rollover(PApplet parent) {
   parent.registerDispose(this);
   runThread = true;
 }

 public void start() {
   thread = new Thread(this);
   thread.start();
 }

 public void run() {
   while(runThread){ //have to loop the run() method
     if(runUpdate){ //only check rollover if we are not calling display()
       for(int i=0; i< buttons.size(); i++){
         Button button = (Button)buttons.get(i);
         button.update(); //we now call this inside the thread
       }
       runUpdate = false; //give display() permission to run
     }
   }
 }
 
 public void stopThread(){
   runThread = false;
 }

 public void stop() {
   thread = null;
 }

 public void dispose() {
   stop();
 }
}


Then, of course, you'll need to make a few slight changes to your main code. Here are some changes you'll need to make to your main sketch. I've commented the areas I've changed:
Code:

ArrayList buttons;

//Thread variables
Rollover rollover; //Declare the new rollover thread class
boolean runUpdate; //This variable makes it so you never
                  //update and display at the same time

void setup() {
 size(400, 400);
 buttons = new ArrayList();
 int numXboxes = 5;
 int numYboxes = 5;
 int totalBoxes = numXboxes * numYboxes;
 int ux = width / numXboxes;
 int uy = height / numYboxes;
 int xcounter = 0;
 int ycounter = 0;

 for(int i = 0 ; i < totalBoxes; i++) {
   buttons.add(new Button((xcounter*ux), (ycounter*uy), ux, color(204), color(255), color(102), i));
   if (xcounter >= (numXboxes-1)) {
     xcounter=0;
     ycounter++;
   }
   else {
     xcounter++;
   }
 }
 
 //Thread setup
 runUpdate = false; //initialize so we display() first, then start updating
 rollover = new Rollover(this); //create new thread
 rollover.start(); //start new thread
}

void draw() {
 background(0);
 if(!runUpdate){ //make sure rollover isn't updating your buttons' statuses
   for(int i=0; i< buttons.size(); i++) {
     Button temp = (Button)buttons.get(i);
     temp.display();
     //notice we are now calling update() in the new thread
   }
   runUpdate = true; //give the other thread permission to check for rollover status
 }
}


Let me know if this works out for you. If you're still getting slow results, it's got to be your drawing routines. Sometimes a quick switch to size(400, 400, OPENGL); will do the trick. Maybe just try that before you go doing all this thread stuff.
Re: There must be a better way to code this.
Reply #5 - Jun 24th, 2009, 1:49pm
 
I don't think that in this case this suggestion will make much of a difference to performance; but I've always worked on the basis that conditions should catch the most likely case first.  So I'd be tempted to change the order of the conditions that Quark copied into the Class's draw method.  Since the most likely state is for 'over' and 'pressed' to be false it would make sense to change it to:

Code:
void draw() {
   if(over == false) {
     fill(baseGray);
   }  
   else if (pressed == true) {  
     fill(pressGray);
   }
   else {
     fill(overGray);          
   }


That way most instances won't go past the first condition.  As I said, in this case you're just checking against booleans and that's hardly going to push the processor...  I suppose this is just a habit to get into for those cases where the code within the conditions is a little more processor intensive Wink
Re: There must be a better way to code this.
Reply #6 - Jun 25th, 2009, 3:57am
 
Firstly, wow, thanks a ton blindfish, NoChinDeluxe, and Quark!

NoChinDeluxe:
This will help a lot. I've spent the past couple hours (time well spent) researching more about threads, because before your post I didn't know they existed. In my original code, display() and update() were running in the same thread, whereas what you're essentially doing here is splitting these two functions into separate threads and thus speeding up both (correct me if I'm wrong, I'm just stating this to help me understand it). But because update() from the thread has to then wait for display() to finish, how is that different from display() then update() both coming from draw() as in the original code? I just ask because I don't completely understand threads yet!

In general, how many threads can be running in a program at the same time? Does each thread run slower than a program that doesn't use threads at all? Or does using a thread open up processing power that was previously unavailable?

Quark:
I come from an Actionscript background, and the code with the MouseEvents looks more like Actionscript - making it easier for me to understand. It also solves a problem that I hadn't posted about yet, namely how my original code would allow clicks to be registered. And yes, no loops in draw()! It seems more intuitive to me to use event listeners rather than for loops...

blindfish:
Your comment about testing conditions more likely to be the case is really useful - do you have other general suggestions about how to streamline code to make it optimised? Or do tutorials exist for this? I've googled, but nothing appropriate has come up for Processing...

All the help is greatly appreciated, thanks once again!!!
Re: There must be a better way to code this.
Reply #7 - Jun 25th, 2009, 7:04am
 
Well I'm still pretty new to threads myself. Quark could probably explain this a little better, because he's the one who originally helped me with this stuff, but here's the way I see it...

In this basic example, the extra thread is obviously overkill. We only have two methods (update() and display()) going back and forth, so yes, you are right when you say it's pretty much the same as drawing in order in the main sketch. But if you have a ton of buttons to check for rollover and to display, I think the system will work a little differently. In your larger project, you wouldn't have a system where it does one update(), then one display(), then moves on to the next button. You would have both update() and display() running at the same time for all your buttons at once.

So look at it this way. You would have access booleans for all your buttons just to ensure a corresponding update() and display() don't run at the exact same time. But that doesn't mean they have to go in order either. The extra thread may be able to check the rollover of all your buttons before you get halfway down the display() list. In other words, the two threads won't necessarily be synchronized. So one thread may be checking the rollover of button 7, while your draw() method is calling display() for button 3. That's what we want. We don't want to interrupt your drawing frames with calls to update(), which is why they are on a different thread. So your draw() method will just keep drawing at its own pace with display(), and your other thread will keep calling update() at it's own pace. Giving them the freedom to do this will speed up the whole process, and we just want to ensure that two corresponding display() and update() methods (for the same button) are not called at the same time.

Wow, horrible explanation.  Lips Sealed But does that make sense to you? In theory, you could be calling multiple update() routines in the time it takes to accomplish one display() routine, which means the whole thing gets sped up, because your draw() method doesn't have to update() before it draws stuff.

ahem...anyway...Quark?  Embarrassed
Re: There must be a better way to code this.
Reply #8 - Jun 25th, 2009, 2:51pm
 
Hi jbucks, NoChinDeluxe and blindfish

Where to start

blindfish said
Quote:
I've always worked on the basis that conditions should catch the most likely case first.  So I'd be tempted to change the order of the conditions that Quark copied into the Class's draw method.  Since the most likely state is for 'over' and 'pressed' to be false it would make sense to change it to:

This is quite correct. In fact if statements can and do reduce performance as modern CPU use pipelines where the instructions 'flow' through the processor and when the boolean condition involves calculation sometimes the processor has to guess the result so later instructions can follow into the pipeline - if it guesses wrong then the pipeline has to be flushed. (This is the Blue Peter version my friend who writes chess programs for computer vs computer games would be able to explain it in in much greater over my head detail). Sorry that was a long aside.

NoChinDeluxe no problem with your description and the code example looked good.

Now I get to quote myself
Quote:
There is always an alternative way but not always a better way

We now have 3 ways, the original pasted by jbucks, threads posted by NoChinDeluxe, and my way using a single thread but utilizing a little known Processing feature with blindfish finding those hard to find logical errors (mouseY should have been mouseX) and providing good programming practice.

So which way is best
jbucks original post rules that way out so what about threads

Well threads are very good for time consuming background tasks and would work well here but as NoChinDeluxe pointed out the update and draw threads need to access the same data i.e. the ArrayList of buttons and this can cause problems because as stated the threads are not synchronized and can cause problems if the draw thread attempts to use data that is currently being modified by the update thread. Unless you are going to have thousands of buttons having the update and draw methods in the same thread is not likely to have a serious effect on performance.

This can be seen here
http://www.lagers.org.uk/g4p/examples.html
this is a demo of my G4P library for Processing and it probably doesn't surprise you that I used the approach I described earlier in this thread.

Each cycle of the Processing loop calls a number of methods
pre() - called once for each registered object
draw() - called once for each registered object
mouseEvent() - this might be called many times as it empties the event queue
post() - called once for each registered object

Iif we want to use these methods it is not enough to just to add them to our sketch we have to register them with e.g.
Code:
registerPre(object); 


where object is the Java object that has the pre() method. (The only exception is the draw()method in our main sketch code)

Including the following in our sketch
Code:
registerPre(this); 


means we want to use the pre() method defined in our main sketch.

In my previous post I simply registered each button in turn for mouseEvent and draw. i.e.
Code:
registerDraw(b);
registerMouseEvent(b);


So its back to you jbucks whether you go for the single thread or multiple threads, both will do want you need and in this particular situation you will not notice any differences in performance between them. I strongly suggest that you pick the one you feel most comfortable with.
Cheesy




Re: There must be a better way to code this.
Reply #9 - Jun 26th, 2009, 2:48am
 
Firstly, NoChinDeluxe, your explanation helped me understand how threads work, so I didn't think it was unclear at all!

@Quark: So if I understand the pre(), draw(), mouseEvents and post() correctly: every loop Processing makes has to go through these stages, and if you register objects, you can check the states (variables, etc) for these registered objects, rather than using for loops - and this can make the program run a bit faster because Processing isn't slowed down (relatively) in one stage by the for loop?

In the end, registering mouse events worked! I tried the thread method, but because the other classes in my more complex code are also working in the background, I got some strange errors probably because the threads were out of sync. But, now that I know more or less how threads work, I'm going to use what NoChinDeluxe and you have taught me here about Threads to handle some other bit of complexity.

All in all, thanks once again to everyone for the explanations and the help with my code! This has been truly a big help!
Re: There must be a better way to code this.
Reply #10 - Jun 26th, 2009, 6:33am
 
As payment for our services, we'll now expect to see some sort of little demo of your project.  Wink
Re: There must be a better way to code this.
Reply #11 - Jun 27th, 2009, 3:06am
 
Sure, when finished I'll post a link! It's actually nothing too major, an interface for my portfolio site, I thought I would create this interface also as a way to learn some more about Processing.
Re: There must be a better way to code this.
Reply #12 - Jun 27th, 2009, 6:31am
 
Yeah Processing is great if you want to kind of create a whole interactive web page. I met LA flash designer Jon Ruppel at a media arts conference where he was showcasing his new portfolio website, oneover.com, and ever since then I've wanted to create something similar in Processing. He said he had heard of processing but never worked with it, so I wanted to one-up his one-over to show him what it could do.
Page Index Toggle Pages: 1