LEDAnimator.java
package ch.ladestation.connectncharge.pui;
import ch.ladestation.connectncharge.model.game.gamelogic.Edge;
import ch.ladestation.connectncharge.model.game.gamelogic.Segment;
import com.github.mbelling.ws281x.Color;
import com.github.mbelling.ws281x.LedStrip;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class LEDAnimator {
public static final Logger LOGGER = LoggerFactory.getLogger(LEDAnimator.class);
private Color[] ledStates;
private List<LEDAnimation> activeAnims;
private LedStrip stripRef;
private ExecutorService executorService;
public LEDAnimator(LedStrip ledStrip) {
this.stripRef = ledStrip;
ledStates = new Color[ledStrip.getLedsCount()];
for (int i = 0; i < ledStates.length; i++) {
ledStates[i] = Color.BLACK;
}
executorService = Executors.newSingleThreadExecutor();
activeAnims = new ArrayList<>();
}
public void frameTick() {
synchronized (ledStates) {
var iter = activeAnims.iterator();
while (iter.hasNext()) {
var current = iter.next();
try {
if (!current.tick(ledStates)) {
iter.remove();
}
} catch (Exception e) {
LOGGER.warn("faulty animation code, removing offender", e);
iter.remove();
}
}
render(ledStates, stripRef);
if (!activeAnims.isEmpty()) {
LOGGER.trace("scheduled new frame");
executorService.submit(this::frameTick);
}
}
}
private void render(Color[] ledStates, LedStrip strip) {
for (int i = 0; i < ledStates.length; i++) {
strip.setPixel(i, ledStates[i]);
}
strip.render();
}
public void simplyToggleSegment(Segment seg, boolean state) {
if (seg == null) {
return;
}
int from = seg.getStartIndex();
int to = seg.getEndIndex();
synchronized (ledStates) {
for (var i = from; i <= to; ++i) {
ledStates[i] = state ? seg.getColor() : Color.BLACK;
}
executorService.submit(this::frameTick);
}
}
public void simplyToggleMultipleSegments(Segment[] newValue, boolean state) {
for (var seg : newValue) {
simplyToggleSegment(seg, state);
}
}
public void scheduleEdgesToBeAnimated(Edge[] oldValues, Edge[] newValues) {
var oldList = new ArrayList<>(Arrays.asList(oldValues));
var newList = new ArrayList<>(Arrays.asList(newValues));
synchronized (ledStates) {
if (oldValues.length > newValues.length) {
oldList.removeAll(newList);
instantiateAnims(oldList, true);
} else if (oldValues.length < newValues.length) {
newList.removeAll(oldList);
instantiateAnims(newList, false);
}
if (!activeAnims.isEmpty()) {
executorService.submit(this::frameTick);
}
}
}
public void instantiateAnims(List<Edge> newList, boolean dissappear) {
for (var edg : newList) {
if (activeAnims.stream().anyMatch(a -> a.getAssociatedEdge() == edg)) {
continue;
}
activeAnims.add(new WipeFromCenterAnimation(edg, dissappear));
}
}
}