Please I want to draw a 3D graph using JavaFX 8 (3D). I already know some basics of 3D like draw sphere, firstly coloring the sphere and add shadow, then some light and initialisation of the sphere. My problem is I want to join the spheres by using a cylinder, but if there are for example 2 cylinders between two spheres it must be an arc or curved cylinder (I don't know if this is possible). I already tried that but nothing appears, even if something appears it's just a cylinder (not like a line just small).
Another problem, I want to know how the rotation can help in such a situation.
And last question, is it possible to make a scrollbar or just using event of Zoom? Thanks.
the picture taken from : Here
This is my code:
import java.util.ArrayList;
import java.util.Random;
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.PerspectiveCamera;
import javafx.scene.PointLight;
import javafx.scene.Scene;
import javafx.scene.paint.Color;
import javafx.scene.paint.PhongMaterial;
import javafx.scene.shape.CullFace;
import javafx.scene.shape.DrawMode;
import javafx.scene.shape.Sphere;
import javafx.stage.Stage;
public class Graphe3D extends Application {
Group root;
PhongMaterial material;
ArrayList<Sphere> sphere;
#Override
public void start(Stage primaryStage) {
sphere=new ArrayList<>();
root=new Group();
material= new PhongMaterial();
for(int i=0;i<3;i++){
sphere.add(new Sphere(50));
//Sphere Color
material.setDiffuseColor(Color.RED);
//Shadow Color
material.setSpecularColor(Color.rgb(30, 30, 30));
//Init Sphere
sphere.get(i).setMaterial(material);
sphere.get(i).setTranslateX(new Random().nextInt(600));//set location X,Y and Z
sphere.get(i).setTranslateY(new Random().nextInt(600));
sphere.get(i).setTranslateZ(50); // ?
sphere.get(i).setDrawMode(DrawMode.FILL);
sphere.get(i).setCullFace(CullFace.BACK);// ?
//Create Light
PointLight pointLight = new PointLight(Color.ANTIQUEWHITE);
pointLight.setTranslateX(800);
pointLight.setTranslateY(-100);
pointLight.setTranslateZ(-1000);
root.getChildren().add(pointLight); //ajout de lumiere
root.getChildren().add(sphere.get(i)); //ajout des spheres au scene(root)
}
//Display
Scene scene = new Scene(root);
scene.setFill(Color.rgb(10, 10, 40));
scene.setCamera(new PerspectiveCamera(false));
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
The molecule sample application from Oracle does much of what you are asking. Read the linked tutorial and study the source code provided for the sample application.
Regarding a couple of your questions.
if there is for example 2 cylinder between two sphere it must an arc or curved cylinder
This is possible. You would have to generate a custom TriangleMesh rather than using the pre-built Cylinder class. Essentially, you need to create an elliptical Torus and only display an arc portion of the Torus between your two nodes. I will not provide detailed instructions on how to do this in the context of a StackOverflow answer.
I want to know how the rotation can help in such situation and last question is it possible to make a scrollbar or just using event of Zoom ?
Study the Molecule sample code linked earlier as that has rotation and zoom capability.
You can calculate the cylinder position and rotation using
Shape.getTransforms().add(new Rotate(angle, x, y, z, Rotate.Z_AXIS))
and
Math.toDegrees(Math.atan2(y, x))
Here's a sample working in 2D
Sphere var = null;
for(Sphere sphere: myListSphere {
if(var!=null) {
double x = sphere.getTranslateX()-var.getTranslateX();
double y = sphere.getTranslateY()-var.getTranslateY();
//the distance from each sphere
Cylinder cyl = new Cylinder(5, Math.sqrt((x*x)+(y*y)), 5);
cyl.setMaterial(this.redMaterial);
cyl.setTranslateX(sphere.getTranslateX()-(x/2));
cyl.setTranslateY(sphere.getTranslateY()-(y/2));
//the angle from both dots with Math.atan
cyl.getTransforms().add(new Rotate(90+Math.toDegrees(Math.atan2(y, x)), 0, 0, 0, Rotate.Z_AXIS));
}
var = shape;
}
Related
I am using a 64 bit linux machine (8GB of RAM) on KDE with Eclipse as my IDE. I am also using Oracle's JDK. I made a small animation using JavaFX and a few pictures off the web to animate earth rotating around the sun. Whenever I run it, the animation works normally, but it steadily eats all of the RAM on my computer until everything freezes. This usually takes less than 5 minutes.
package Practice;
/**
* For some reason, this code gobbles up memory, and freezes computers
*/
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.image.Image;
import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class BasicAnimation extends Application {
public BasicAnimation() {
// TODO Auto-generated constructor stub
}
public void start(Stage primaryStage) throws Exception {
primaryStage.setTitle("Orbit");
Group root = new Group();
Scene scene = new Scene(root);
primaryStage.setScene(scene);
Canvas canvas = new Canvas(512,512);
root.getChildren().add(canvas);
GraphicsContext gc = canvas.getGraphicsContext2D();
Image earth = new Image(getClass().getResourceAsStream("earth.png"), 25.0, 25.0 ,false, false);
Image sun = new Image(getClass().getResourceAsStream("sun.jpg"), 50.0, 50.0, false, false);
Image space = new Image(getClass().getResourceAsStream("space.jpg"));
final long startNanoTime = System.nanoTime();
new AnimationTimer() {
public void handle(long currentNanoTime) {
double t = (currentNanoTime - startNanoTime) / 1000000000.0 ;
double x = 232 + 128 * Math.cos(t);
double y = 232 + 128 * Math.sin(t);
//background image clears canvas
gc.drawImage(space, 0, 0);
gc.drawImage(earth, x, y);
gc.drawImage(sun, 196, 196);
}
}.start();
primaryStage.show();
}
}
I've set -Xms512m, -Xmx512m, and -Xss512m. Is there something I'm doing wrong that could be causing this, and could you explain why that happens or how to avoid it?
Also if there is something wrong with my question, please let me know.
Edits: Added more information
The Earth image is 2356x2356, and I set it to 25x25px in the program. The Sun image is 750x750, and I set it to 50x50 in the program . The space image is 1920x1080, and it is the background which is 512x512 px.
Links to images
Sun : https://www.thesun.co.uk/wp-content/uploads/2016/06/download.jpg?w=750&strip=all
Earth : https://openclipart.org/image/2400px/svg_to_png/218125/3d-Earth-Globe.png
Space : http://www.gunnars.com/wp-content/uploads/2014/08/Space.jpg
I can't see anything wrong in your code. It may not be the best way to do this in JavaFX but it looks perfectly valid to me and should not eat up any memory. Especially as you say that you have the same problem with the other code from Luke I suspect some Linux bug. Have you tried running your program on another OS? If you'd provide the links to your images someone else could try that too.
This link may be related to your problem:
javafx-unexplainable-leaks-memory-on-linux
Test
I ran your program on a Mac and there was no memory leak and almost no CPU usage, as I'd expected.
gc.drawImage(space, 0, 0); is causing the problem. IMHO, you are not supposed to call it for every frame.
Normally we do animation by rendering frames. We get a Graphics object and for each frame we clear the canvas and redraw everything. But that's not how things work in JavaFX.
In JavaFX, animation is achieved by applying transformation on nodes - shapes, images or groups. You setup the scene, add your "actors", the shapes, images etc. Then you create Animation objects that control those "actors".
I am no expert, so the following example just demonstrate the idea of how to make a circle rotate around another. The motion is not uniform. So you definitely want to experiment different paths/Transitions/Animations.
Update: use Path.setInterpolator(Interpolator.LINEAR) to remove the acceleration
import javafx.animation.PathTransition;
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.effect.BoxBlur;
import javafx.scene.paint.Color;
import javafx.scene.shape.*;
import javafx.stage.Stage;
import javafx.util.Duration;
import static javafx.animation.Animation.INDEFINITE;
public class Animation extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
primaryStage.setTitle("orbit");
Group root = new Group();
Scene scene = new Scene(root, 800, 800, Color.BLACK);
primaryStage.setScene(scene);
Circle sun = new Circle(50, Color.web("yellow", 1.0));
sun.setCenterX(400);
sun.setCenterY(400);
sun.setEffect(new BoxBlur(10, 10, 3));
Circle earth = new Circle(10, Color.web("blue"));
earth.setEffect(new BoxBlur(4, 4, 3));
root.getChildren().add(sun);
root.getChildren().add(earth);
Path path = new Path();
ArcTo arcTo = new ArcTo();
arcTo.setX(20);
arcTo.setY(401);
arcTo.setSweepFlag(true);
arcTo.setLargeArcFlag(true);
arcTo.setRadiusX(400);
arcTo.setRadiusY(400);
arcTo.setXAxisRotation(0);
path.getElements().add(new MoveTo(20, 400));
path.getElements().add(arcTo);
path.getElements().add(new ClosePath());
path.setVisible(false);
PathTransition pt = new PathTransition(Duration.seconds(10), path, earth);
pt.setInterpolator(Interpolator.LINEAR); // No acceleration/deceleration
pt.setOrientation(PathTransition.OrientationType.ORTHOGONAL_TO_TANGENT);
pt.setCycleCount(INDEFINITE);
pt.play();
primaryStage.show();
}
}
tl;dr Use the -D.prism=sw argument for the JVM to hack around the problem.
JavaFX tries to make use of hardware acceleration, and it seems that the new Mesa and Xorg drivers don't entirely solve the issue. I also think that the VDPAU driver, the hardware acceleration driver, is at fault. When I run the program with a -D.prism=sw argument for the JVM, which sets the compiler to use software pipelining rather than hardware acceleration, the problem is greatly reduced. I still see that the program steadily consumes memory, but the process is much slower.
I also found that reducing the number of times the gc.drawimage() is called also increases the amount of time it takes to fill my RAM.
new AnimationTimer() { //This is how I reduced the number of times gc.drawImage is called
long lastupdate = 0 ;
public void handle(long currentNanoTime) {
double t = (currentNanoTime - startNanoTime) / 1000000000.0 ;
double x = 232 + 128 * Math.cos(t);
double y = 232 + 128 * Math.sin(t);
//background image clears canvas
if(currentNanoTime - lastupdate >= 33333333) {
gc.clearRect(0, 0, canvas.getWidth(), canvas.getHeight());
gc.drawImage(space, 0, 0);
gc.drawImage(sun, 196, 196);
gc.drawImage(earth, x, y);
lastupdate = currentNanoTime;
}
}
}.start();
There may be an issue with garbage collection there, but I'm not sure. I will use VisualVM to check later, then I'll update this answer.
Update
There is no issue with garbage collection. The memory is stable now, and it was stabilized by enabling software pipelining. JavaFX seems to not work well with VDPAU. Thanks for all the help!
Sources:
https://github.com/jfoenixadmin/JFoenix/issues/52
https://bugs.openjdk.java.net/browse/JDK-8161997
I've been looking for solutions in google with no success, I'm creating a small library (just a wrapper) for Box2D in LibGDX and I'm drawing a texture for each body, taking as base the Body.getPosition() vector, however, I see polygon's getPosition() is different respect to CircleShapes and the walls (which were created with setAsBox() method).
Here's an image:
http://i.stack.imgur.com/NzooG.png
The red points are the center of mass, the cyan circles are the geometric center (right?) given by body.getPosition(), as you can see I can adapt the texture to the body in terms of position, rotation and scale but this does not happen with polygons (except the ones made with setAsBox()).
Basically, what I want is to get the cyan circle in the AABB centre of the regular polygons. here's a runnable example:
import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.math.Vector2;
public class MyGdxGame extends ApplicationAdapter {
Tabox2D t;
float w ,h;
#Override
public void create () {
w = Gdx.graphics.getWidth();
h = Gdx.graphics.getHeight();
t = Tabox2D.getInstance();
t.debug = true;
t.newBall("d", 100, 200, 25);// Ball.
t.newBox("s", 10, 10, w - 20, 50);// Floor.
t.newHeptagon("d", new Vector2(200, 200), 40);
}
#Override
public void render () {
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
t.update(0);// Don't move anything just to see the cyan circle.
t.draw();
}
}
Tabox2D class is here: https://github.com/tavuntu/tabox2d
NOTE: this was tested with the last version of Android Studio and LibGDX.
Thanks in advance!
Looks to me like you are positioning the shapes on the centre point, instead of the body. The centre of the shapes should be 0,0, not center.x,y. Fixture positions are relative to the body.
OK, so I think I solved this maybe in an inelegant way. What I did was:
Create the polygons points around the origin
Get AABB center of polygon and centroid (not necessarily the same)
Translate points to make AABB center the shape center, so the image can be drawn respect to this and not the center of mass.
The code needs a lot of cleaning but it seems to work just well, updated code in github repo, thanks!
The change was basically translate points from the original centroid to AABB center:
for(int i = 0; i < pts.length - 1; i += 2) {
pts[i] -= aabbCenter.x;
pts[i + 1] -= aabbCenter.y;
}
I've been googling and searching, found some some related questions/posts but none of the address my problem.
I am drawing lines directly on canvas (JavaFX) using:
gc.setStroke(color);
gc.setLineWidth(lineWidth);
gc.strokeLine(startX, startY, endX, endY);
I want 1 pixel width lines. So I set lineWidth=1.
I get this:
Note that the lines are blurred. It is not 1 pixel.
I've tried to set lineWidth to 0.1 or 0.01, etc. It does not change the result.
By the way... I do not understand why this parameter is a double. I read somewhere that it has to do with DPI. But I do not understand what is the unit and how it is converted to pixels.
Oracle's documentation does not help. (or I did not find the one that helps)
I'd like to get this instead:
This was implemented in another platform. Note that lines are sharp and have just one 1 pixel.
Imagine each pixel as a (small) rectangle (instead of a point). The integer coordinates are the boundaries between pixels; so a (horizontal or vertical) line with integer coordinates falls "between pixels". This is rendered via antialising, approximating half of the line on one pixel and half on the other. Moving the line 0.5 pixels left or right moves it to the center of the pixel, getting around the issue.
Here's a sample:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class SharpCanvasTest extends Application {
#Override
public void start(Stage primaryStage) {
Canvas sharpCanvas = createCanvasGrid(600, 300, true);
Canvas blurryCanvas = createCanvasGrid(600, 300, false);
VBox root = new VBox(5, sharpCanvas, blurryCanvas);
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
private Canvas createCanvasGrid(int width, int height, boolean sharp) {
Canvas canvas = new Canvas(width, height);
GraphicsContext gc = canvas.getGraphicsContext2D() ;
gc.setLineWidth(1.0);
for (double x = sharp ? 0.5 : 0.0; x < width; x+=10) {
gc.moveTo(x, 0);
gc.lineTo(x, height);
gc.stroke();
}
for (double y = sharp ? 0.5 : 0.0; y < height; y+=10) {
gc.moveTo(0, y);
gc.lineTo(width, y);
gc.stroke();
}
return canvas ;
}
public static void main(String[] args) {
launch(args);
}
}
And the results:
Use coordinates in this notation x.5.
Look my example:
gc.setFill(Color.BLACK);
gc.setLineWidth(1.0);
gc.strokeRect(50, 100, 25.0, 25.0);
gc.strokeRect(100.5, 100.5, 25.0, 25.0);
You will get two squares, the second sharp.
Reference: https://dlsc.com/2014/04/10/javafx-tip-2-sharp-drawing-with-canvas-api/
When I use a ShapeRenderer, it always comes out pixelated. But if I draw the shape in photoshop with the same dimensions, it's very smooth and clean-looking.
My method is just as follows:
package com.me.actors;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer.ShapeType;
import com.badlogic.gdx.scenes.scene2d.Actor;
public class bub_actors extends Actor {
private ShapeRenderer shapes;
private Texture text;
private Sprite sprite;
public bub_actors(){
shapes = new ShapeRenderer();
text = new Texture(Gdx.files.internal("data/circle.png"));
sprite = new Sprite();
sprite.setRegion(text);
}
#Override
public void draw(SpriteBatch batch, float parentAlpha) {
batch.draw(sprite, 200, 200, 64, 64);
shapes.begin(ShapeType.FilledCircle);
shapes.filledCircle(50, 50, 32);
shapes.setColor(Color.BLACK);
shapes.end();
}
}
Here's an image of the output:
Any ideas as to why this happens? Is it possible to make the ShapeRenderer look like the image (so I don't have to create a SpriteBatch of different-colored circles...).
The difference is anti-aliasing that Photoshop applies to the image it generates. If you zoom in on the edges of the two circles, you'll see the anti-aliased one has some semi-black pixels around the edge, where the ShapeRenderer generated circle just shows pixels entirely on or off.
The Libgdx ShapeRenderer was designed for being a quick and simple way to get debugging shapes on the screen, it does not support anti-aliasing. The easiest way to get consistent anti-aliased rendering it to use a texture. (Its also possible with an OpenGL shader.)
That said, you do not have to create different sprites just to render different colored circles. Just use a white circle with a transparent background, and then render it with a color. (Assuming you want a variety of solid-colored circles).
Here is really simple way to achieve smooth & well-looking shapes without using a texture and SpriteBatch.
All you have to do is to render couple of shapes with slightly larger size and
lower alpha channel along with the first one.
The more passes the better result, but, of course, consider ppi of your screen.
...
float alphaMultiplier = 0.5f; //you may play with different coefficients
float radiusStep = radius/200;
int sampleRate = 3;
...
//do not forget to enable blending
Gdx.gl.glEnable(GL20.GL_BLEND);
Gdx.gl.glBlendFunc(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA);
shapeRenderer.begin(ShapeType.Filled);
//first rendering
shapeRenderer.setColor(r, g, b, a);
shapeRenderer.circle(x, y, radius);
//additional renderings
for(int i=0; i<sampleRate; i++) {
a *= alphaMultiplier;
radius += radiusStep;
shapeRenderer.setColor(r, g, b, a);
shapeRenderer.circle(x, y, radius);
}
shapeRenderer.end();
...
Here is a screenshot of what can you achieve.
If you're not teetering on the edge of losing frames, you can enable antialiasing in your launcher. You can increase the sample count for better results, but it's really diminishing returns.
LWJGL3 : config.setBackBufferConfig(8, 8, 8, 8, 16, 0, 2);
LWJGL2 : config.samples = 2;
GWT : config.antialiasing = true;
Android: config.numSamples = 2;
iOS : config.multisample = GLKViewDrawableMultisample._4X;
I got the head, one arm and the body. I am trying to make another arm using the same first two coordinates, which starts at the bottom of the head, but a negative last (but same number) last two coordinates. I assumed that if I made a negative version, it would just make an opposite version of the line. Instead, its just sticking straight up! I am confused on why this is happening.
import javax.swing.JComponent;
import java.awt.*;
import java.awt.geom.*;
public class StickFigure extends JComponent
{
public void paintComponent(Graphics g)
{
Graphics2D g2 = (Graphics2D) g;
Ellipse2D.Double head = new Ellipse2D.Double(5, 10, 50, 50);
g2.draw(head);
Line2D.Double body=new Line2D.Double(30,60, 30,150);
g2.draw(body);
Line2D.Double arm1=new Line2D.Double(30,60,75,75);
g2.draw(arm1);
Line2D.Double arm2=new Line2D.Double(30,60,-75,-75);
g2.draw(arm2);
}
}
That is the code that is giving me trouble. I am using a viewer which is the following:
import javax.swing.JFrame;
public class Viewer
{
public static void main(String[] arg)
{
JFrame frame = new JFrame();
frame.setSize(1000,500);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
StickFigure fig1=new StickFigure();
frame.add(fig1);
frame.setVisible(true);
}
}
Please let me know what I am doing wrong, I would greatly appreciate it.
Line2D.Double arm2=new Line2D.Double(30,60,-75,-75);
You need to think about what you're saying with -75 and -75. Remember those make a coordinate, and (0, 0) represents the top left corner in Swing (unless you're explicitly telling it not to). Those coordinates are offscreen to the northwest.
Try something like:
Line2D.Double arm2=new Line2D.Double(30,60, 45,75);
Try using a positive y last coordinate for both:
Line2D.Double arm1=new Line2D.Double(30,60,75,75);
g2.draw(arm1);
Line2D.Double arm2=new Line2D.Double(30,60,-75,75);
g2.draw(arm2);
You are right that -75 -75 "would just make an opposite version of the line", but when you alter both coordinates you get radial simmetry, that is simmetry around a point (the neck) hence one of your arm is low and the other is up. You want axial symmetry in this case, and for that you only need to flip one coordinate; since people's axis of symmetry is the spine, and it is vertical (y-direction) you need to flip coordinate x only.