Multiple viewports in javafx application - java

I am attempting to create a multi-user, multi-screen application within JavaFX, and I am having trouble with the multi-screen part.
Think an FPS with couch co-op: the screen splits evenly depending on how many people are connected locally. Every different view is looking in a different direction, and at a different place, but at the same 'world'.
I learned the hard way (confirmed in a comment here) that each node can only appear in the active scene graph once, so, for instance, I cannot have the same node spread across multiple distinct panes (which is conceptually ideal). And that's where I'm not sure where to go next.
Looking at other similar technologies like OpenGL, (example) most have the ability to create another viewport for the application, but JavaFX does not seem to have this.
Some things I have ruled out as unreasonable/impossible (correct me if I'm wrong):
Using shapes to create a clip mask for a pane (Can only use one mask per node)
Having a complete deep copy of each node for each view (too expensive, nodes moving constantly)
Having x number of users each have their own set of nodes and have one update loop update every node in every view (too expensive, too many nodes for scene graph, too much)
How would I go about creating multiple views of the same set of nodes, while still maintaining individual user control, and changing persistence/moving nodes, between every different view?
Thanks.

Thanks to the people in the comments for the solution. I ended up creating a background model for each view to mirror, and then creating a new set of nodes per view that has the relevant properties bound to the background model.
The update loop then has to only update the one background model, and the copies all update automatically. Each node copy has a reference to the model node that it is mimicking, so when a user inputs a change for a node, the model node is changed, which changes the copy node.
The solution is not too elegant and I will have to look more into multithreading (multitasking?) with Tasks (here) and Platform.runLater() (here) functions of JavaFX to increase functionality.
Here is a quick example of what I accomplished:
Main.java
public class Main extends Application {
private static Group root = new Group();
private static Scene initialScene = new Scene(root, Color.BLACK);
private static final int NUM_OF_CLIENTS = 8;
private static long updateSpeed = 20_666_666L;
private static double deltaTime;
private static double counter = 0;
#Override
public void start(Stage primaryStage) {
primaryStage.setFullScreen(true);
primaryStage.setScene(initialScene);
primaryStage.show();
initModel();
initModelViews();
startUpdates();
}
private void initModel() {
for (int i = 0; i < NUM_OF_CLIENTS; i++) {
Model.add(new UpdateObject());
}
}
private void initModelViews() {
//Correctly positioning the views
int xPanes = (NUM_OF_CLIENTS / 4.0 > 1.0) ? 4 : NUM_OF_CLIENTS;
int yPanes = (NUM_OF_CLIENTS / 4) + ((NUM_OF_CLIENTS % 4 > 0) ? 1 : 0);
for (int i = 0; i < NUM_OF_CLIENTS; i++) {
Pane clientView = new Pane(copyModelNodes());
clientView.setBackground(new Background(new BackgroundFill(Color.color(Math.random(), Math.random(), Math.random()), CornerRadii.EMPTY, Insets.EMPTY)));
System.out.println(clientView.getChildren());
clientView.relocate((i % 4) * (Main.initialScene.getWidth() / xPanes), (i / 4) * (Main.initialScene.getHeight() / yPanes)) ;
clientView.setPrefSize((Main.initialScene.getWidth() / xPanes), (Main.initialScene.getHeight() / yPanes));
root.getChildren().add(clientView);
}
}
private Node[] copyModelNodes() {
ObservableList<UpdateObject> model = Model.getModel();
Node[] modelCopy = new Node[model.size()];
for (int i = 0; i < model.size(); i++) {
ImageView testNode = new ImageView();
testNode.setImage(model.get(i).getImage());
testNode.layoutXProperty().bind(model.get(i).layoutXProperty());
testNode.layoutYProperty().bind(model.get(i).layoutYProperty());
testNode.rotateProperty().bind(model.get(i).rotateProperty());
modelCopy[i] = testNode;
}
return modelCopy;
}
private void startUpdates() {
AnimationTimer mainLoop = new AnimationTimer() {
private long lastUpdate = 0;
#Override
public void handle(long frameTime) {
//Time difference from last frame
deltaTime = 0.00000001 * (frameTime - lastUpdate);
if (deltaTime <= 0.1 || deltaTime >= 1.0)
deltaTime = 0.00000001 * updateSpeed;
if (frameTime - lastUpdate >= updateSpeed) {
update();
lastUpdate = frameTime;
}
}
};
mainLoop.start();
}
private void update() {
counter += 0.1;
if (counter > 10.0) {
counter = 0;
}
for (UpdateObject objectToUpdate : Model.getModel()) {
objectToUpdate.setLayoutX(objectToUpdate.getLayoutX() + 0.02 * counter * deltaTime);
objectToUpdate.setLayoutY(objectToUpdate.getLayoutY() + 0.02 * counter * deltaTime);
objectToUpdate.setRotate(objectToUpdate.getRotate() + 5);
}
}
}
UpdateObject.java
class UpdateObject extends ImageView {
private static Random random = new Random();
private static Image testImage = new Image("duckTest.png");
UpdateObject() {
this.setImage(testImage);
this.setLayoutX(random.nextInt(50));
this.setLayoutY(random.nextInt(50));
this.setRotate(random.nextInt(360));
}
}
Model.java
class Model {
private static ObservableList<UpdateObject> modelList = FXCollections.observableArrayList();
static void add(UpdateObject objectToAdd) {
modelList.add(objectToAdd);
}
static ObservableList<UpdateObject> getModel() {
return modelList;
}
}
Test image used

Related

How do I change the speed of an object moved by keyboard input inside an AnimationTimer?

I have this JavaFX Circle, which moves according to keyboard's arrows.
All the AnimationTimer does is refreshing the circle position every frame.
I found a movement of 0.1 every time a KeyEvent is triggered to be smooth enough for the animation, however it moves really slow. On the other hand if I change the movement to let's say 1.0 or 10.0, it's undoubtedly faster, but also much more choppy (you can clearly see it starts moving by discrete values).
I want to be able to keep the smoothness of translating at most 0.1 per frame, but also be able to change how much space it should move every time a key is triggered.
Below is an mre describing the problem:
public class MainFX extends Application {
private double playerX;
private double playerY;
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
AnchorPane pane = new AnchorPane();
Scene scene = new Scene(pane, 800, 600);
primaryStage.setScene(scene);
primaryStage.show();
playerX = pane.getWidth()/2;
playerY = pane.getHeight()/2;
Circle player = new Circle(playerX,playerY,10);
pane.getChildren().add(player);
scene.addEventHandler(KeyEvent.KEY_PRESSED, this::animate);
AnimationTimer timer = new AnimationTimer() {
#Override
public void handle(long now) {
player.setCenterX(playerX);
player.setCenterY(playerY);
}
};
timer.start();
}
private void animate(KeyEvent key){
if (key.getCode() == KeyCode.UP) {
playerY-=0.1;
}
if (key.getCode() == KeyCode.DOWN) {
playerY+=0.1;
}
if (key.getCode() == KeyCode.RIGHT) {
playerX+=0.1;
}
if (key.getCode() == KeyCode.LEFT) {
playerX-=0.1;
}
}
}
The AnimationTimer's handle() method is invoked on every frame rendering. Assuming the FX Application thread is not overwhelmed with other work, this will occur at approximately 60 frames per second. Updating the view from this method will give a relatively smooth animation.
By contrast, the key event handlers are invoked on every key press (or release, etc.) event. Typically, when a key is held down, the native system will issue repeated key press events at some rate that is system dependent (and usually user-configurable), and typically is much slower that animation frames (usually every half second or so). Changing the position of UI elements from here will result in jerky motion.
Your current code updates the position of the UI element from the playerX and playerY variables in the AnimationTimer: however you only change those variable is the key event handlers. So if the AnimationTimer is running at 60fps, and the key events are occurring every 0.5s (for example), you will "update" the UI elements 30 times with each new value, changing the actual position only two times per second.
A better approach is to use key event handlers merely to maintain the state of variables indicating if each key is pressed or not. In the AnimationTimer, update the UI depending on the state of the keys, and the amount of time elapsed since the last update.
Here is a version of your code using this approach:
import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.geometry.Point2D;
import javafx.scene.Scene;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.AnchorPane;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
public class MainFX extends Application {
private boolean leftPressed ;
private boolean rightPressed ;
private boolean upPressed ;
private boolean downPressed ;
private static final double SPEED = 100 ; // pixels/second
private static final double PLAYER_RADIUS = 10 ;
private AnchorPane pane;
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
pane = new AnchorPane();
Scene scene = new Scene(pane, 800, 600);
primaryStage.setScene(scene);
primaryStage.show();
double playerX = pane.getWidth() / 2;
double playerY = pane.getHeight() / 2;
Circle player = new Circle(playerX, playerY, PLAYER_RADIUS);
pane.getChildren().add(player);
scene.addEventHandler(KeyEvent.KEY_PRESSED, this::press);
scene.addEventHandler(KeyEvent.KEY_RELEASED, this::release);
AnimationTimer timer = new AnimationTimer() {
private long lastUpdate = System.nanoTime() ;
#Override
public void handle(long now) {
double elapsedSeconds = (now - lastUpdate) / 1_000_000_000.0 ;
int deltaX = 0 ;
int deltaY = 0 ;
if (leftPressed) deltaX -= 1 ;
if (rightPressed) deltaX += 1 ;
if (upPressed) deltaY -= 1 ;
if (downPressed) deltaY += 1 ;
Point2D translationVector = new Point2D(deltaX, deltaY)
.normalize()
.multiply(SPEED * elapsedSeconds);
player.setCenterX(clampX(player.getCenterX() + translationVector.getX()));
player.setCenterY(clampY(player.getCenterY() + translationVector.getY()));
lastUpdate = now ;
}
};
timer.start();
}
private double clampX(double value) {
return clamp(value, PLAYER_RADIUS, pane.getWidth() - PLAYER_RADIUS);
}
private double clampY(double value) {
return clamp(value, PLAYER_RADIUS, pane.getHeight() - PLAYER_RADIUS);
}
private double clamp(double value, double min, double max) {
return Math.max(min, Math.min(max, value));
}
private void press(KeyEvent event) {
handle(event.getCode(), true);
}
private void release(KeyEvent event) {
handle(event.getCode(), false);
}
private void handle(KeyCode key, boolean press) {
switch(key) {
case UP: upPressed = press ; break ;
case DOWN: downPressed = press ; break ;
case LEFT: leftPressed = press ; break ;
case RIGHT: rightPressed = press ; break ;
default: ;
}
}
}
You aren't using the animation timer properly.
Your key code processing should be used to set a velocity for the object. Set the velocity to 0 when the key is released.
In the animation timer code, change the position based on the elapsed time and the velocity.
The timer will fire events at the frame rate - likely around 60fps. If you want smooth motion you want to adjust the position on every frame. Instead you are using it to set the position to a pre-computed value. It isn't doing anything useful that way. You could just as easily set the position in the key processing code and get the same effect you are getting now.
If you don't want to have the user hold down the keys to move. That is, you want to tap once and have the object move by 10.0. You can set the target position in the key processing code. Jumping the target position by 10 at a time. Then have the animation timer move the current position towards the target position at an appropriate velocity, stopping when the target position is reached.
Maybe something like this:
AnimationTimer timer = new AnimationTimer() {
#Override
public void handle(long now) {
double curX = player.getCenterX();
double curY = player.getCenterY();
double diffX = playerX-curX;
double diffY = playerY-curY;
if (diffX > 1.0) {
curX += 1.0;
else if (diffX < -1.0) {
curX -= 1.0;
} else {
curX = playerX;
}
if (diffY > 1.0) {
curY += 1.0;
else if (diffY < -1.0) {
curY -= 1.0;
} else {
curY = playerY;
}
player.setCenterX(curX);
player.setCenterY(curY);
}
};
That's a primitive example... and note that it will make diagonal movements that go faster than axis-aligned movements. (The velocity vector magnitude for diagonal movements is sqrt(2) in that example instead of 1.)
Basically you want to update the position based on a velocity vector adjusted for the interval between ticks of the animation timer.

How to properly remove images for animations in JavaFX

im trying to create a simple space invaders game. This is my first ever game, and so im having some issues. The invaders is a rectangle object with an image of the oldschool invader. In each frame im drawing those boxes 15 pixels to the right, and then using root.getChildren().remove(), to remove the sprites in each frame. But it causes some weird behaviour. It kind of looks like it isnt being removed quickly enough, and so just when i launch the app it kind of explodes, and after that there seems to be a little bit of latency causing two images to be displayed constantly.
I guess it would probably help for you to see it yourself and so i will post all of my files here.
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
primaryStage.setTitle("Space invaders");
primaryStage.setResizable(false);
Group root = new Group();
Scene game = new Scene(root, Color.BLACK);
primaryStage.setFullScreen(true);
Images images = new Images();
// Make canvas and add it to primaryStage
Canvas canvas = new Canvas(1280,720);
root.getChildren().add(canvas);
GraphicsContext gc = canvas.getGraphicsContext2D();
new AnimationTimer() {
int move = 0;
private long lastUpdate = 0;
ArrayList<Sprite> enemies = new ArrayList<>();
#Override
public void handle(long now) {
if (now - lastUpdate >= 150_000_000) {
int spacing = 0;
try {
for (int i = 0; i < enemies.size(); i++) {
root.getChildren().remove(enemies.get(i));
enemies.remove(i);
}
} catch (IndexOutOfBoundsException ioobe) {
System.out.println(ioobe.toString());
}
for (int i = 0; i < 12; i++) {
for (int j = 0; j < 6; j++) {
Sprite enemy = new Sprite(60 * i + spacing + move, 43 * j, 60, 43, "enemy", true, images.getEnemy1());
enemies.add(enemy);
root.getChildren().add(enemy);
}
spacing += 15;
}
move += 5;
lastUpdate = now;
}
}
}.start();
primaryStage.setScene(game);
primaryStage.show();
}
}
If you want to run the program yourself, her is the Sprite class:
public class Sprite extends Rectangle {
private String type;
private Boolean alive;
public Sprite(int x, int y, int w, int h, String type, boolean alive, Image image) {
this.setTranslateX(x);
this.setTranslateY(y);
this.setWidth(w);
this.setHeight(h);
this.type = type;
this.alive = alive;
this.setFill(new ImagePattern(image));
}
public String getType() {
return type;
}
public Boolean getAlive() {
return alive;
}
}
This is just not the way the scene graph is supposed to be used. Why do you constantly remove and re-add your sprites? Why don't you just update their positions via the setTranslateX/Y which you are already using anyway?
try {
for (int i = 0; i < enemies.size(); i++) {
root.getChildren().remove(enemies.get(i));
enemies.remove(i);
}
} catch (IndexOutOfBoundsException ioobe) {
System.out.println(ioobe.toString());
}
A correct implementation wouldn't require you to catch a IndexOutOfBoundsException. I don't know why this would throw such an exception though, since the check i < enemies.size() should ensure the index is valid.
Note however that this skips every other list element:
First you remove the first element of enemies from both lists (the one at index 0). This results in all elements remaining in the enemies list to be shifted to the left, i.e. the one that was at index 1 before the removal is now at index 0. Since you increment the index after each loop iteration, all elements at odd indices are skipped.
There's a simpler way of implementing this btw:
root.getChildren().removeAll(enemies);
enemies.clear();
Note
Recreating your sprites in every frame should be avoided. The memory requirement of Nodes doesn't make them objects you should throw away, if this can be avoided. Otherwise the garbage collector will be kept busy cleaning up the objects you "throw" away at best and an OutOfMemoryException happens at worst...
Reuse the sprites and modify the properties instead.
Also I recommend going with ImageView instead of Rectangles filled with ImagePattern. You can adjust the size using the fitWidth and fitHeight properties.

JavaFX: updating progress for the multiple tasks

I'm writing a multithreaded fractal drawing program with JavaFX 2.2 and now I need some guidance.
What I'm trying to achieve is to create a Task or Service (haven't decided yet) which then fires up some other tasks that actually do the calculation and return sections of the whole image when ready. When all the pieces are returned to the initiating task it puts together the pieces and returns it to the main thread so it can be visualized.
Obviously, all this must happen without ever blocking the UI.
The problem is I can't figure out how these tasks could communicate with each other. For example, I need to update the progress property of the initiating task based on the average progress of the tasks inside it (or something like this), so their progress properties should be bound to the progress property of the initiating task somehow. The image pieces should be put in a list or some container and redrawn on a separate image when all of them are available.
I have already written a simpler (though still experimental) version of this program that creates only one task that calculates the whole fractal. The progress is bound to the progressBar of the GUI. The return value is handled by an EventHandler on success of the task.
I'm not asking for a complete solution but some ideas with maybe a little bit of example code would really help me.
This is the class that should be modified:
package fractal;
import fractalUtil.DefaultPalette;
import fractalUtil.PaletteInterface;
import javafx.concurrent.Task;
import javafx.scene.image.WritableImage;
import javafx.scene.paint.Color;
import org.apache.commons.math3.complex.Complex;
/**
*
* #author athelionas
*/
public abstract class AbstractFractal extends Task implements FractalInterface {
private PaletteInterface palette;
protected final int width, height, order, iterations;
protected final double scale, xReal, xIm, xCenter, yCenter, zoom;
protected final boolean julia;
protected AbstractFractal(final int width, final int height, final double xReal, final double xIm, final double xCenter, final double yCenter, final int order, final boolean julia, final int iterations, final double zoom) {
this.width = width;
this.height = height;
this.xReal = xReal;
this.xIm = xIm;
this.xCenter = xCenter;
this.yCenter = yCenter;
this.order = order;
this.julia = julia;
this.iterations = iterations;
this.zoom = zoom;
this.scale = (double) width / (double) height;
palette = new DefaultPalette();
}
#Override
public final void setPalette(final PaletteInterface palette) {
this.palette = palette;
}
#Override
public abstract Complex formula(final Complex z, final Complex c, final int order, final Complex center);
#Override
public final Color calculatePoint(final Complex z, final Complex c, final int order, final Complex center, final int iterations) {
Complex zTemp = z;
int iter = iterations;
while (zTemp.abs() <= 2.0 && iter > 0) {
zTemp = formula(zTemp, c, order, center);
iter--;
}
if (iter == 0) {
return Color.rgb(0, 0, 0);
} else {
return palette.pickColor((double) (iterations - iter) / (double) iterations);
}
}
#Override
public final WritableImage call() {
Complex z;
Complex c;
Complex center = new Complex(xCenter, yCenter);
final WritableImage image = new WritableImage(width, height);
if (julia) {
c = new Complex(xReal, xIm);
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
z = new Complex(((double) x) / (double) (width - 1) * 2.0 * scale * (1.0 / zoom) - scale * (1.0 / zoom), ((double) y) / (double) (height - 1) * 2.0 * (1.0 / zoom) - 1.0 * (1.0 / zoom));
image.getPixelWriter().setColor(x, y, calculatePoint(z, c, order, center, iterations));
}
}
} else {
z = new Complex(xReal, xIm);
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
c = new Complex(((double) x) / (double) (width - 1) * 2.0 * scale * (1.0 / zoom) - scale * (1.0 / zoom), ((double) y) / (double) (height - 1) * 2.0 * (1.0 / zoom) - 1.0 * (1.0 / zoom));
image.getPixelWriter().setColor(x, y, calculatePoint(z, c, order, center, iterations));
}
updateProgress(y, height);
}
}
return image;
}
}
Use binding and Task. This way you don't need to care about threading at all. All you need is to create a binding which will normalize each progress according to threads number and summ them up. E.g.
progressBar.progressProperty().bind(
task1.progressProperty().multiply(0.5).add(
task2.progressProperty().multiply(0.5)));
It's a bit trickier for unknown number of threads. See next example:
public class MultiProgressTask extends Application {
private static final int THREADS_NUM = 10;
// this is our Task which produces a Node and track progress
private static class MyTask extends Task<Node> {
private final int delay = new Random().nextInt(1000) + 100;
{ System.out.println("I update progress every " + delay); }
#Override
protected Node call() throws Exception {
updateProgress(0, 5);
for (int i = 0; i < 5; i++) {
System.out.println(i);
Thread.sleep(delay); // imitating activity
updateProgress(i+1, 5);
}
System.out.println("done");
return new Rectangle(20, 20, Color.RED);
}
};
#Override
public void start(Stage primaryStage) {
ProgressBar pb = new ProgressBar(0);
pb.setMinWidth(300);
final VBox root = new VBox();
root.getChildren().add(pb);
Scene scene = new Scene(root, 300, 250);
primaryStage.setScene(scene);
primaryStage.show();
DoubleBinding progress = null;
for (int i = 0; i < THREADS_NUM; i++) {
final MyTask mt = new MyTask();
// here goes binding creation
DoubleBinding scaledProgress = mt.progressProperty().divide(THREADS_NUM);
if (progress == null) {
progress = scaledProgress;
} else {
progress = progress.add(scaledProgress);
}
// here you process the result of MyTask
mt.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
#Override
public void handle(WorkerStateEvent t) {
root.getChildren().add((Node)t.getSource().getValue());
}
});
new Thread(mt).start();
}
pb.progressProperty().bind(progress);
}
public static void main(String[] args) { launch(args); }
}
This is a pretty interesting problem :)
If we remove the issue of thread safety for a moment, you could pass in a double property (or whatever the progress property is bound to) and update that with the progress which would then update the progress indicator. Two problems with that:
Multiple tasks could increment the property at the same time.
The changes must be fired on the javafx thread.
I would wrap the property in it's own class with a simple API:
class ProgressModel {
private final SimpleDoubleProperty progress;
public void increment(finally double increment) {
Platform.runLater(new Runnable() {
progress.set(progress.doubleValue() + increment);
}
}
public void bindPropertyToProgress(DoubleProperty property) {
property.bind(progress);
}
}
In the above code, all updates will run on the javafx thread sequentially so it is thread safe plus no locks. I have done similar background tasks like this and performance has been good (realtime to the user's eyes) although if you're updating thousands of times a second this might not be the case! You will just need to measure. I've not shown the boiler plate code to make it a bit more readable.

Problems reading data from 3 Kinect cameras

I am trying to read data from multiple Kinect sensors (3 at the moment) and having issues when there's more than 2 devices.
I'm using Daniel Shiffman's OpenKinect Processing wrapper, slightly modified so it allows to open multiple Device instances. Everything works fine with 2 devices. The problem is when I use 3. One Kinect is connected straight into one of the available two usb ports, and the other two are plugged into a USB 2.0 Hub (that has it's own power adapter).
The devices all initialize succesfully:
org.openkinect.Device#1deeb40 initialized
org.openkinect.Device#2c35e initialized
org.openkinect.Device#1cffeb4 initialized
The problem is when I try to get the depth map from the 3rd device, I get an array filled with zeroes. I've thought it's the device, but if swap devices, it's always the 3rd (last connected device) that presents this behaviour.
Here's my code so far:
package librarytests;
import org.openkinect.Context;
import org.openkinect.processing.Kinect;
import processing.core.PApplet;
import processing.core.PVector;
public class PointCloudxN extends PApplet {
// Kinect Library object
int numKinects;// = 3;
Kinect[] kinects;
int[] colours = {color(192,0,0),color(0,192,0),color(0,0,192),color(192,192,0),color(0,192,192),color(192,0,192)};
// Size of kinect image
int w = 640;
int h = 480;
// We'll use a lookup table so that we don't have to repeat the math over and over
float[] depthLookUp = new float[2048];
// Scale up by 200
float factor = 200;
public void setup() {
size(800,600,P3D);
numKinects = Context.getContext().devices();
kinects = new Kinect[numKinects];
for (int i = 0; i < numKinects; i++) {
kinects[i] = new Kinect(this);
kinects[i].start(i);
kinects[i].enableDepth(true);
kinects[i].processDepthImage(false);
}
// Lookup table for all possible depth values (0 - 2047)
for (int i = 0; i < depthLookUp.length; i++) {
depthLookUp[i] = rawDepthToMeters(i);
}
}
public void draw() {
background(0);
translate(width/2,height/2,-50);
rotateY(map(mouseX,0,width,-PI,PI));
rotateX(map(mouseY,0,height,-PI,PI));
int skip = 4;//res
//*
for (int i = 0; i < numKinects; i++) {
Kinect kinect = kinects[i];
int[] depth = kinect.getRawDepth();
//if(frameCount % 60 == 0 && i == 2) println(depth);
if (depth != null) {
// Translate and rotate
for(int x=0; x<w; x+=skip) {
for(int y=0; y<h; y+=skip) {
int offset = x+y*w;
// Convert kinect data to world xyz coordinate
int rawDepth = depth[offset];
PVector v = depthToWorld(x,y,rawDepth);
stroke(colours[i]);
// Draw a point
point(v.x*factor,v.y*factor,factor-v.z*factor);
}
}
}
}
//*/
}
public void stop() {
for (int i = 0; i < numKinects; i++) kinects[i].quit();
super.stop();
}
public static void main(String _args[]) {
PApplet.main(new String[] { librarytests.PointCloudxN.class.getName() });
}
// These functions come from: http://graphics.stanford.edu/~mdfisher/Kinect.html
float rawDepthToMeters(int depthValue) {
if (depthValue < 2047) {
return (float)(1.0 / ((double)(depthValue) * -0.0030711016 + 3.3309495161));
}
return 0.0f;
}
PVector depthToWorld(int x, int y, int depthValue) {
final double fx_d = 1.0 / 5.9421434211923247e+02;
final double fy_d = 1.0 / 5.9104053696870778e+02;
final double cx_d = 3.3930780975300314e+02;
final double cy_d = 2.4273913761751615e+02;
PVector result = new PVector();
double depth = depthLookUp[depthValue];//rawDepthToMeters(depthValue);
result.x = (float)((x - cx_d) * depth * fx_d);
result.y = (float)((y - cy_d) * depth * fy_d);
result.z = (float)(depth);
return result;
}
}
The only major change I've done to Daniel's Kinect class was adding an extra start() method:
public void start(int id) {
context = Context.getContext();
if(context.devices() < 1)
{
System.out.println("No Kinect devices found.");
}
device = context.getDevice(id);
//device.acceleration(this);
device.acceleration(new Acceleration()
{
void Acceleration(){
System.out.println("new Acceleration implementation");
}
public void direction(float x, float y, float z)
{
System.out.printf("Acceleration: %f %f %f\n", x ,y ,z);
}
});
kimg = new RGBImage(p5parent);
dimg = new DepthImage(p5parent);
running = true;
super.start();
}
I've also tried with MaxMSP/Jitter and the jit.freenect external and I get the same behaviour: I can get 2 depth maps, but the 3rd is not updating.
So it seems to be an issue related to the driver, not the wrapper, since the same behaviour is present using 2 different wrappers to libfreenect (Java/Processing and Max), but am clueless why this happens to be honest.
Has anyone had a similar issue (getting depth feeds from 3 devices) using the OpenKinect/libfreenect Driver ? Any ideas on how I can get past this issue ?
The Kinect is extremely demanding on USB - generally, you can only get one Kinect per USB host controller on your motherboard (most PCs and laptops have two). The only solution I've seen is to buy a PCI-E USB controller and plug the third one into it.
Also, you might be lucky if you reduce the bandwidth requirements by disabling the RGB stream on all the Kinects (I'm blithely assuming you aren't using it since it wasn't mentioned)

How to programatically pan a VisualizationViewer with Jung (the java library)?

After a lot a investigations, I don't achieve to find a convenient answer to the following question: how to programatically pan a VisualizationViewer with Jung?
I have a GUI with the list of the vertices of my graph, and I want that a double click on one item of the list (i.e. a node description) perform a "centering action" of my VisualizationViewer for the clicked node.
How to code such a behavior? it seems simple but I found no convenient answer.
If someone could help, thanks!
njames
Here is how to popup a menu after right-clicking on a node in JUNG2 and later choose to center to this node:
graphMouse.add(new MyPopupGraphMousePlugin());
/**
* a GraphMousePlugin that offers popup
* menu support
*/
protected class MyPopupGraphMousePlugin extends AbstractPopupGraphMousePlugin
implements MouseListener {
public MyPopupGraphMousePlugin() {
this(MouseEvent.BUTTON3_MASK);
}
public MyPopupGraphMousePlugin(int modifiers) {
super(modifiers);
}
/**
* If this event is over a node, pop up a menu to
* allow the user to center view to the node
*
* #param e
*/
protected void handlePopup(MouseEvent e) {
final VisualizationViewer<Node,Link> vv =
(VisualizationViewer<Node,Link>)e.getSource();
final Point2D p = e.getPoint();
GraphElementAccessor<Node,Link> pickSupport = vv.getPickSupport();
if(pickSupport != null) {
final Node station = pickSupport.getVertex(vv.getGraphLayout(), p.getX(), p.getY());
if(station != null) {
JPopupMenu popup = new JPopupMenu();
String center = "Center to Node";
popup.add(new AbstractAction("<html><center>" + center) {
public void actionPerformed(ActionEvent e) {
MutableTransformer view = vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.VIEW);
MutableTransformer layout = vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT);
Point2D ctr = vv.getCenter();
Point2D pnt = view.inverseTransform(ctr);
double scale = vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.VIEW).getScale();
double deltaX = (ctr.getX() - p.getX())*1/scale;
double deltaY = (ctr.getY() - p.getY())*1/scale;
Point2D delta = new Point2D.Double(deltaX, deltaY);
layout.translate(deltaX, deltaY);
}
});
popup.show(vv, e.getX(), e.getY());
} else {
}
}
}
}
Edited: Finally! the correct node-to-center-view with scale factor calculation...
public void centerViewsOnVertex(SynsetVertex v) {
//the following location have sense in the space of the layout
Point2D v_location = layout.transform(v);
Point2D vv1_center_location = vv1.getRenderContext()
.getMultiLayerTransformer()
.inverseTransform(Layer.LAYOUT, vv1.getCenter());
double scale = vv1.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.VIEW).getScale();
vv1.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT).translate(
-(v_location.getX() - vv1_center_location.getX()) * 1
/ scale,
-(v_location.getY() - vv1_center_location.getY()) * 1
/ scale);
refreshViews();
}
Since I was just looking for an answer to this and the above worked terribly; here's a code snippet I found in AnimatedPickingGraphMousePlugin that will recenter:
Layout<V,E> layout = vv.getGraphLayout();
Point2D q = layout.transform(vertex);
Point2D lvc =
vv.getRenderContext().getMultiLayerTransformer().inverseTransform(vv.getCenter());
final double dx = (lvc.getX() - q.getX());
final double dy = (lvc.getY() - q.getY());
vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT).translate(dx, dy);
Actually, I have found by the next a solution:
//the following location have sense in the space of the layout
Point2D v_location = layout.transform(v);
Point2D vv1_center_location = vv1.getRenderContext()
.getMultiLayerTransformer()
.inverseTransform(Layer.LAYOUT, vv1.getCenter());
vv1.getRenderContext()
.getMultiLayerTransformer()
.getTransformer(Layer.LAYOUT)
.translate(-(v_location.getX() - vv1_center_location.getX()),
-(v_location.getY() - vv1_center_location.getY()));
The center on node function is already implemented in the AnimatedPickingGraphMousePlugin
http://sourceforge.net/p/jung/discussion/252062/thread/18b4b941
In picking mode ctrl+click on a vertex to center on it.
#SuppressWarnings("unchecked")
public void mouseReleased(MouseEvent e) {
if (e.getModifiers() == modifiers) {
final VisualizationViewer<V,E> vv = (VisualizationViewer<V,E>) e.getSource();
if (vertex != null) {
Layout<V,E> layout = vv.getGraphLayout();
Point2D q = layout.transform(vertex);
Point2D lvc = vv.getRenderContext().getMultiLayerTransformer().inverseTransform(vv.getCenter());
final double dx = (lvc.getX() - q.getX()) / 10;
final double dy = (lvc.getY() - q.getY()) / 10;
Runnable animator = new Runnable() {
public void run() {
for (int i = 0; i < 10; i++) {
vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT).translate(dx, dy);
try {
Thread.sleep(100);
} catch (InterruptedException ex) {
}
}
}
};
Thread thread = new Thread(animator);
thread.start();
}
}
}
I was trying to figure out how to center on a given vertex and came across this, but unfortunately it was not particularly helpful and I spent a fair amount of time figuring out how to do it. So, sharing my experience here in case it may be helpful for others.
The application I'm writing has a VisualizationViewer, that is loaded inside a GraphZoomScrollPane. I have a GraphMouseListener that I've added to the VisualizationViewer, which allows a user to right click on a vertex in the viewable area of the scroll pane and in the popup menu they can choose to center on the vertex.
The top voted answer on this thread references usage of a MutableTransformer from the LAYOUT layer and it uses the translate method of that transformer to do the centering action. Unfortunately, if you are using a zoom/scroll then you don't really know the size and positioning of the layout layer in relation to the view layer without doing a bunch of extra math.
When using zoom/scroll pane, I'd recommend finding the location of the vertex in the viewable area of the graph as represented by the pane, and then adjusting where the view pane is at.
Here is a snippet of the code I worked out:
void center(MouseEvent me, GraphZoomScrollPane gzsp) {
VisualizationViewer<V,E> vv =
(VisualizationViewer<V,E>)me.getSource();
MutableTransformer viewTransformer =
vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.VIEW);
double scaleFromViewTransformer = viewTransformer.getScale();
Dimension paneSize = gzsp.getSize();
Point2D positionOfVertexInPane = me.getPoint();
double[] centerOfPane = new double[] {
paneSize.getWidth()/2d,
paneSize.getHeight()/2d
};
double[] amountToMovePane = new double[] {
(centerOfPane[0]-positionOfVertexInPane.getX())/scaleFromViewTransformer,
(centerOfPane[1]-positionOfVertexInPane.getY())/scaleFromViewTransformer
};
viewTransformer.translate(amountToMovePane[0], amountToMovePane[1]);
}

Categories

Resources