B/c there’s a slightly time loss during the time done is set to true
and we check it ourselves, and only then we re-invoke start(), I don’t think class Countdown fits fully the requirements of your “sequencers”.
Just made a variant of it called Interval, where done is set to true
over & over at a fixed period time.
We now need to call Interval::intervalDone() in order to check whether the previous interval had been completed.
As a bonus, in place or in addition to check Interval::intervalDone(), we can also assign our own customized Interval.Callback::run() to the field Interval::callback.
Well, better see it for yourself. Here’s the code for both the “.pde” example and the “.java” library itself:
“IntervalClass.pde”:
/**
* Interval Class (v1.0)
* GoToLoop (2018/Jul/09)
*
* https://Discourse.Processing.org/t/
* sequencer-firing-multiple-events-or-one-step-behind/1571/12
*/
import gotoloop.time.Interval;
static final float SECS = 1;
static final int WAIT_TIME = (int) (SECS * 1000);
final Interval interval = new Interval(WAIT_TIME);
color bg;
void setup() {
size(300, 180);
smooth(3);
frameRate(60);
colorMode(RGB);
fill(#FFFF00);
textSize(0100);
textAlign(CENTER, CENTER);
final int m = millis(), t = m + WAIT_TIME;
interval.start();
println(m, t, t - m, TAB, interval);
createCallback();
}
void draw() {
if (interval.intervalDone()) bg = randomColor();
background(bg);
final String txt = millis() + "\n" + frameCount;
text(txt, width>>1, height>>1);
}
color randomColor() {
return (color) random(PImage.ALPHA_MASK);
}
void createCallback() {
interval.callback = new Interval.Callback() {
@Override public void run() {
fill(randomColor());
}
};
}
“Interval.java”:
/**
* Interval Class (v1.0)
* GoToLoop (2018/Jul/09)
*
* https://Discourse.Processing.org/t/
* sequencer-firing-multiple-events-or-one-step-behind/1571/12
*/
package gotoloop.time;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.atomic.AtomicBoolean;
public class Interval {
protected static final Timer t = new Timer("Interval");
protected final AtomicBoolean done = new AtomicBoolean(true);
public Callback callback;
public TimerTask task;
public int period;
public boolean precise = true;
public Interval() {
}
public Interval(final int waitTime) { // milliseconds
this(waitTime, null);
}
public Interval(final int waitTime, final Callback listener) {
period = Math.abs(waitTime);
callback = listener;
}
@Override public String toString() {
return "Period: " + period;
}
public Interval start() {
stop().done.set(false);
task = new Timeout();
if (precise) t.scheduleAtFixedRate(task, period, period);
else t.schedule(task, period, period);
return this;
}
public Interval stop() {
if (task != null) task.cancel();
return this;
}
public boolean intervalDone() {
return done.compareAndSet(true, false);
}
@FunctionalInterface public interface Callback {
void run();
}
protected class Timeout extends TimerTask {
@Override public void run() {
done.set(true);
if (callback != null) callback.run();
}
}
}