Jump to content

onPaint caveats


Recommended Posts

Posted (edited)
onPaint caveats

 

Description of onPaint: "called when the script should paint it's debug".

We are generously going to assume the onPaint(Graph) method gets called 30 times per seconds.



@Override
public void onPaint(Graphics2D g2d) {
super.onPaint(g2d);
// Insert custom behavior here.
}


 

1. The method's thread of execution should never be ceased or paused

 

          This will lead to inaccurate or lagged rendering, stuttering and an overall unpleasant experience for your end-user.

 



@Deprecated
@Override
public void onPaint(Graphics2D g2d) {
super.onPaint(g2d);
/*
* BAD CODE!
*/
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}


If your IDE notifies you about an unhandled InterruptedException in your onPaint method, you're probably doing something wrong.

 

2. Calculate values at efficient intervals

 

 Always keep in mind that this method gets called 30 times per seconds however:

 

- Accurate calculation of elapsed seconds requires only one or two calculations per second.

 

- Other calculations (experience, profit, etc...) require even less than that.

 

3. ... and cache them where possible

 

Avoid recalculation of values (such as elapsed time) used in other calculations, store and re-use where possible!

 

Demonstration of caveats 2 and 3:

 



@ScriptManifest(author = "Traum", info = "Tutorial", logo = "", name = "Tutorial - Paint", version = 0)
public class TutorialPaint extends Script {

// The String component that will be rendered.
private String paintcomponentTime = "Time:";

// We store the starting system's clock time in milliseconds.
private long startMilliseconds = System.currentTimeMillis();

// We store the elapsed amount of milliseconds (so we can reuse it instead of having to recalculate it when necessary.
private long elapsedMilliseconds = 0;

// We use this single thread executor to automatically recalculate / update the paint-required values (every 500 milliseconds).
private ScheduledExecutorService paintValueExecutor = Executors.newSingleThreadScheduledExecutor();

// The runnable that will be executed by the executor.
private Runnable paintValueRunnable = () -> {
// We calculate the elapsed amount of milliseconds.
elapsedMilliseconds = System.currentTimeMillis() - startMilliseconds;
// We also build the string value of the component that our onPaint method is going to draw, doing this here will save the cost of rapid repeated concatenation.
paintcomponentTime = "Time: " + elapsedMilliseconds;
};

@Override
public void onStart() throws InterruptedException {
super.onStart();
paintValueExecutor.scheduleAtFixedRate(paintValueRunnable, 0, 500, TimeUnit.MILLISECONDS);
}

@Override
public int onLoop() throws InterruptedException {
// ...
return 200;
}


@Override
public void onPaint(Graphics2D g2d) {
super.onPaint(g2d);
// No object or value is being calculated or created here! (except for the mandatory pixel arrays and stuff like that of course).
g2d.drawString(paintcomponentTime, 50, 50);
}

@Override
public void onExit() throws InterruptedException {
super.onExit();
paintValueExecutor.shutdown();
}

}


4. Avoid transparency

 

Avoid using transparency in your colors and images, your CPU and those of your end-users will thank me later.

 Only use transparency cleverly where it improves usability, don't use it just for the looks of it.

 

More to come soon, feel free to suggest your best onPaint() tip(s)!

 

 

 

Edited by Botre
  • Like 1
Posted

Caching the Strings isnt really necessary, since they are added to the String pool.

 

In other words

 

You aren't gaining any more efficiency by doing 

 

 
private String timeString = "Time:";
 
public void print(){
      for(int i = 0; i < 5; i ++){
          System.out.println(timeString);
      }
}

 

compared to

 

 
public void print(){
      for(int i = 0; i < 5; i ++){
          System.out.println("Time");
      }
}

 

However, it would still be better practice in my opinion, since if you wanted to change it, you would only have to change it in one place.

Posted (edited)

Caching the Strings isnt really necessary, since they are added to the String pool.

 

Thread A changes String X twice per second.

Thread B reads String X 30 times per second.

 

The reason I'm putting String X in a variable is so it's available to both thread A & thread B, not for caching purposes (since Strings are pretty much automatically cached internally (like you just pointed out :p)).

Edited by Botre
Posted

Sooo scheduling an additional thread is not more expensive than some minor computations? 

 

You're asking me if:

calculation_cost + constant_scheduling_overhead_cost < calculation_cost * 15

Well, that depends entirely on the cost of the calculations per call and how much the scheduling overhead actually is.

 

Once the total cost of all calculations starts exceeding (constant_scheduling_overhead_cost / 14) it becomes worth using an additional thread from a performance POV.

 

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...