Reputation: 21
So i have a small application that displays a molecule. In the corner I have a second scene that shows the axis and the axis rotation relative to the main scene. At the end of each axis I want text saying which axis it is, labels are a,b,c (think x,y,z but the input is in fractional coordinates)).
When rotating the scene I am able to keep the a,b,c at the right positions, and they generally do face (lookAt) the camera, but they rotate as I move across certain axis's.
[Initial Position - labels are orientated correctly] (https://i.sstatic.net/9LTQz.png) After rotating - labels flip
So the labels are MyLabels extends GridPane with the text in, they have a setTransforms function which gives the result of look at, as well as a default scale, and translation based on the position of the end of the axis.
public class MyLabel extends GridPane {
public Point3D position;
double scaleFactor =-0.05;
Scale scale = new Scale(scaleFactor,scaleFactor,scaleFactor,0,0,0);
Translate translate;
public MyLabel(String message, Point3D position) {
System.out.println("new label: " + message + " " + position);
Text label = new Text(message);
Font font = new Font("arial", 20);
label.setFont(font);
label.setCache(true);
setAlignment(Pos.CENTER);
this.position = position;
CuboidMesh contentShape = new CuboidMesh(0,0, 0.01);
GridPane.setHalignment(label, HPos.CENTER);
add(label, 3, 1);
double w = contentShape.getWidth() * 10; // more resolution
double h = contentShape.getHeight() * 10;
double d = contentShape.getDepth() * 10;
final double W = 2 * d + 2 * w;
final double H = 2 * d + h;
ColumnConstraints col1 = new ColumnConstraints();
col1.setPercentWidth(d * 100 / W);
ColumnConstraints col2 = new ColumnConstraints();
col2.setPercentWidth(w * 100 / W);
ColumnConstraints col3 = new ColumnConstraints();
col3.setPercentWidth(d * 100 / W);
ColumnConstraints col4 = new ColumnConstraints();
col4.setPercentWidth(w * 100 / W);
getColumnConstraints().addAll(col1, col2, col3, col4);
RowConstraints row1 = new RowConstraints();
row1.setPercentHeight(d * 100 / H);
RowConstraints row2 = new RowConstraints();
row2.setPercentHeight(h * 100 / H);
RowConstraints row3 = new RowConstraints();
row3.setPercentHeight(d * 100 / H);
getRowConstraints().addAll(row1, row2, row3);
setPrefSize(W, H);
setBackground(new Background(new BackgroundFill(Color.YELLOW, CornerRadii.EMPTY, Insets.EMPTY)));
translate = new Translate(this.position.getX(),this.position.getY(),this.position.getZ());
}
public void setTransforms(Transform affine) {
try {
getTransforms().setAll(affine);
getTransforms().add(scale);
getTransforms().add(translate);
} catch (Exception e) {
e.printStackTrace();
}
}
}
My look at function is:
public static Affine lookAt(Point3D from, Point3D to, Point3D ydir) {
Point3D zVec = to.subtract(from).normalize();
Point3D xVec = ydir.normalize().crossProduct(zVec).normalize();
Point3D yVec = zVec.crossProduct(xVec).normalize();
Affine affine = new Affine(xVec.getX(), 0, zVec.getX(), from.getX(),
xVec.getY(), yVec.getY(), zVec.getY(), from.getY(),
xVec.getZ(), yVec.getZ(), zVec.getZ(), from.getZ());
return affine;
}
And when the camera is moved, I update the lables using:
Point3D randomPoint = new Point3D(0,0,-20); //camera initially starts 0,0,-50
Point3D camera = cameraXform.localToScene(randomPoint);
myLabel.setTransforms(lookAt(myLabel.position,camera,new Point3D(0, 1,0)));
cameraXform is the group containing the two cameras (axis camera and main scene camera), which has a transform that is continually added to, this is how the rotation happens on mouse moving.
class XformCamera extends Group {
Point3D px = new Point3D(1.0, 0.0, 0.0);
Point3D py = new Point3D(0.0, 1.0, 0.0);
Rotate r;
Transform t = new Rotate();
public XformCamera() {
super();
}
public void rx(double angle) {
// System.out.println("rx: " + angle);
r = new Rotate(angle, px);
this.t = t.createConcatenation(r);
this.getTransforms().clear();
this.getTransforms().addAll(t);
}
public void ry(double angle) {
//System.out.println("ry: " + angle);
r = new Rotate(angle, py);
this.t = t.createConcatenation(r);
this.getTransforms().clear();
this.getTransforms().addAll(t);
}
}
After a lot of work, the labels stay at the correct translation, they face the eye the majority of the time but occasionally they will flip (the flip happens quickly but not instantly, i.e. it will be facing fine then for some 10degree arc it will completely flip around then face again (flipped) until it hits the next 10degree madness about 80degress later.
I think what I need to do is work out which way is up for the eye (but in 2d?), and make sure the label is perpedicular to it after the lookAt, but i am now a bit out of my depth to work it out.
Any help much appreciated!
I've tried using Fxyz however I struggled to find any documentation on it. Ive used localToScene, localToScreen, localToParent with no luck. If i pass the cameraXForm to the label it faces perfectly, however is not translated into the correct positions. I would also be happy where the labels are put in 2d ontop of the scene, and just update their x,y, coordinates depending where the axis's are, for this i would need to get the 2d scene coordinates for the 3d point in the group.
Upvotes: 2
Views: 105
Reputation: 795
The right way to do this is using floating 2D labels transformed from 3D anchor points.
I've tried using Fxyz
Did you? because we have an example in our samples module that does precisely what you want:
Floating Labels Example in FXyz3D
Important notes for this solution:
however I struggled to find any documentation on it.
Well... its tough finding even more spare time to make fancy documentation when nobody's paying you to write all this stuff and then give it away for free. (This is a shot at other people whom come to our github and complain) However we did make a fully interactive sampler app for a large cross section of non trivial examples:
Interactive Sampler provided by FXyz3D
GLHF. The other FXyz3D guys are nicer than me.
Upvotes: 0