JavaFX 2.2 MouseEvent in SubScene does not work properly - javafx-2

I am trying to get this to work for sometime and I cannot figure out what wrong with my code. This leads me to believe there is some issues in SubScene Mouse listener. Any idea is appreciated.
Basically I have a scene contains two subscenes, one for the toolbar and one for the floor which has bunch of lines making it looks like tiles. I added mouse listeners so that when I clicked on the floor and move the mouse, the camera will move as if I am walking on the floor.
The problem is that the floor only recognize mouse event when I clicked on the intersection between the first vertical and the first horizontal line (yup, took me a while to figure that out). Mouse event should occur everywhere on entire floor.
Here is the code.
import javafx.application.Application;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.PerspectiveCamera;
import javafx.scene.Scene;
import javafx.scene.SceneAntialiasing;
import javafx.scene.SubScene;
import javafx.scene.control.Button;
import javafx.scene.input.MouseEvent;
import javafx.scene.shape.Line;
import javafx.stage.Stage;
public class FloorTest extends Application {
double mousex, mousey;
#Override
public void start(Stage primaryStage) {
Button btn = new Button();
btn.setText("Say 'Hello World'");
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
System.out.println("Hello World!");
}
});
Group bargroup = new Group();
SubScene bar = new SubScene(bargroup, 300, 20, true, SceneAntialiasing.DISABLED);
bargroup.getChildren().add(btn);
Group floorgroup = new Group();
SubScene floor = new SubScene(floorgroup, 300, 250, true, SceneAntialiasing.DISABLED);
ObservableList<Node> list = floorgroup.getChildren();
for(int i = 0; i < (300/20); i++)
{
double x = i * 20;
Line line = new Line(x, 0, x, 250);
list.add(line);
}
for(int i = 0; i < (250/20); i++)
{
double y = i * 20;
Line line = new Line(0, y, 300, y);
list.add(line);
}
PerspectiveCamera camera = new PerspectiveCamera(false);
camera.setNearClip(0.1);
camera.setFarClip(10000.0);
camera.setTranslateZ(-200);
floor.setCamera(camera);
floor.setOnMousePressed((MouseEvent event) -> {
mousex = event.getSceneX();
mousey = event.getSceneY();
});
floor.setOnMouseDragged((MouseEvent event) -> {
double x = event.getSceneX();
double y = event.getSceneY();
camera.relocate(camera.getLayoutX() + (x - mousex), camera.getLayoutY() + (y - mousey));
});
Group mainroot = new Group();
mainroot.getChildren().addAll(floor, bar);
Scene scene = new Scene(mainroot, 300, 250, true);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* The main() method is ignored in correctly deployed JavaFX application.
* main() serves only as fallback in case the application can not be
* launched through deployment artifacts, e.g., in IDEs with limited FX
* support. NetBeans ignores main().
*
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}

It seems that setting the subscene.setPickOnBounds(true) should help in proper recognition of the mouse events for the whole subscene. Tested with javafx 8.

Please try using scene instead of floor variable in event handlers
Example:
scene.setOnMousePressed((MouseEvent event) -> {
mousex = event.getSceneX();
mousey = event.getSceneY();
});
scene.setOnMouseDragged((MouseEvent event) -> {
double x = event.getSceneX();
double y = event.getSceneY();
camera.relocate(camera.getLayoutX() + (x - mousex), camera.getLayoutY() + (y - mousey));
});
That helps me

Related

how to pass value to paint method

I am just at very beginnig in Java coding, so far I could solve my problems finding solutions on internet, but this time I just stucked.
I want to create a grid(pattern) of filled rectangles, and it works fine, but then I would like to change colour of rectangles on specific position, and this part is just not working. I have no idea why, what am I doing wrong ?
// **file : MainWindow.java **
import java.util.Scanner;
import javax.swing.JFrame;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
public class MainWindow {
JFrame frame = new JFrame();
int pos_X;
int pos_Y;
// constructor for frame
public MainWindow (String title) {
frame.setSize(1000, 1000);
frame.setTitle(title);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
for (int i=0;i<26;i++){ // works fine
for (int j=0;j<26;j++){
Shape shape = new Shape (110+i*20,110+j*20,19,19,5,100,220); //(x,y, width,height, R-colour, G-colour, B-colour)
frame.add(shape);
frame.setVisible(true); }}
pos_X= 15;
pos_Y = 15;
Shape shape = new Shape (110+pos_X*20,110+pos_Y*20,19,19,250,0,20); // it doesn't work ???
frame.add(shape);
frame.setVisible(true);
}
public static void main(String[] args){
new MainWindow(" my window ");
}
}
// **file : Shape.java **
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.Color;
import java.awt.Color.*;
import javax.swing.JComponent;
public class Shape extends JComponent{
int width, height, xcoord, ycoord, col_r, col_g,col_b;
//constructor
public Shape (int x, int y ,int w,int h, int k, int l, int m)
{
this.width = w;
this.height = h;
this.xcoord = x;
this.ycoord = y;
this.col_r = k;
this.col_g = l;
this.col_b = m;
}
public void paintComponent(Graphics g){
Graphics2D g2d = (Graphics2D) g;
Color x= new Color( col_r,col_g, col_b );
g.setColor(x);
g.fillRect(xcoord, ycoord, width, height);
}
}
If I understand right, what you want could be done ensuring the shape has drawn after the grid, so
pos_X= 15;
pos_Y = 15;
Shape shape = new Shape (110+pos_X*20,110+pos_Y*20,19,19,250,0,20); // it doesn't work ???
frame.setComponentZOrder(shape,0)
frame.add(shape);
frame.setVisible(true);
...
please note the
frame.setComponentZOrder(shape,0)
which ensure that the colored shape is drawn above the others.
In alternative your
P.S.
if it doesn't work try frame.setComponentZOrder(shape,frame.getComponentCount())
See also here for an alternative

JavaFx 2 - Draw line overlap axis

With a left mouse click then mouse moved, a line is drawned on this line chart, and also on axis.
I would like to draw line only on chart so that it does not overlap x or y axis. How to accomplish this?
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.geometry.Side;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.chart.CategoryAxis;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.scene.control.Label;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Line;
import javafx.scene.shape.LineTo;
import javafx.scene.shape.MoveTo;
import javafx.scene.shape.Path;
import javafx.stage.Stage;
public class LinesEdit extends Application {
Path path;
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) {
final CategoryAxis xAxis = new CategoryAxis();
final NumberAxis yAxis = new NumberAxis(1, 21, 0.1);
yAxis.setTickUnit(1);
yAxis.setPrefWidth(35);
yAxis.setMinorTickCount(10);
yAxis.setSide(Side.RIGHT);
yAxis.setTickLabelFormatter(new NumberAxis.DefaultFormatter(yAxis) {
#Override
public String toString(Number object) {
String label;
label = String.format("%7.2f", object.floatValue());
return label;
}
});
final LineChart<String, Number> lineChart = new LineChart<String, Number>(xAxis, yAxis);
lineChart.setCreateSymbols(false);
lineChart.setAlternativeRowFillVisible(false);
lineChart.setLegendVisible(false);
XYChart.Series series1 = new XYChart.Series();
series1.getData().add(new XYChart.Data("Jan", 1));
series1.getData().add(new XYChart.Data("Feb", 4));
series1.getData().add(new XYChart.Data("Mar", 2.5));
series1.getData().add(new XYChart.Data("Apr", 5));
series1.getData().add(new XYChart.Data("May", 6));
series1.getData().add(new XYChart.Data("Jun", 8));
series1.getData().add(new XYChart.Data("Jul", 12));
series1.getData().add(new XYChart.Data("Aug", 8));
series1.getData().add(new XYChart.Data("Sep", 11));
series1.getData().add(new XYChart.Data("Oct", 13));
series1.getData().add(new XYChart.Data("Nov", 10));
series1.getData().add(new XYChart.Data("Dec", 20));
BorderPane bp = new BorderPane();
bp.setCenter(lineChart);
Scene scene = new Scene(bp, 800, 600);
lineChart.setAnimated(false);
lineChart.getData().addAll(series1);
LinesEdit.MouseHandler mh = new LinesEdit.MouseHandler( bp );
bp.setOnMouseClicked( mh );
bp.setOnMouseMoved( mh );
stage.setScene(scene);
path = new Path();
path.setStrokeWidth(1);
path.setStroke(Color.BLACK);
scene.setOnMouseDragged(mh);
scene.setOnMousePressed(mh);
bp.getChildren().add(path);
stage.setScene(scene);
stage.show();
}
class MouseHandler implements EventHandler< MouseEvent > {
private boolean gotFirst = false;
private Line line;
private Pane pane;
private double x1, y1, x2, y2;
private LineHandler lineHandler;
public MouseHandler( Pane pane ) {
this.pane = pane;
lineHandler = new LineHandler(pane);
}
class LineHandler implements EventHandler< MouseEvent > {
double x, y;
Pane pane;
public LineHandler(Pane pane){
this.pane = pane;
}
#Override
public void handle( MouseEvent e ) {
Line l = (Line) e.getSource();
// remove line on right click
if( e.getEventType() == MouseEvent.MOUSE_PRESSED
&& e.isSecondaryButtonDown() ) {
pane.getChildren().remove( l );
} else if( e.getEventType() == MouseEvent.MOUSE_DRAGGED
&& e.isPrimaryButtonDown() ) {
double tx = e.getX();
double ty = e.getY();
double dx = tx - x;
double dy = ty - y;
l.setStartX( l.getStartX() + dx );
l.setStartY( l.getStartY() + dy );
l.setEndX( l.getEndX() + dx );
l.setEndY( l.getEndY() + dy );
x = tx;
y = ty;
} else if( e.getEventType() == MouseEvent.MOUSE_ENTERED ) {
// just to show that the line is selected
x = e.getX();
y = e.getY();
l.setStroke( Color.RED );
} else if( e.getEventType() == MouseEvent.MOUSE_EXITED ) {
l.setStroke( Color.BLACK );
}
// should not pass event to the parent
e.consume();
}
}
#Override
public void handle( MouseEvent event ) {
if( event.getEventType() == MouseEvent.MOUSE_CLICKED ) {
if( !gotFirst ) {
x1 = x2 = event.getX();
y1 = y2 = event.getY();
line = new Line( x1, y1, x2, y2 );
pane.getChildren().add( line );
gotFirst = true;
}
else {
line.setOnMouseEntered( lineHandler );
line.setOnMouseExited( lineHandler );
line.setOnMouseDragged( lineHandler );
line.setOnMousePressed( lineHandler );
// to consume the event
line.setOnMouseClicked( lineHandler );
line.setOnMouseReleased( lineHandler );
line = null;
gotFirst = false;
}
}
else {
if( line != null ) {
x2 = event.getX();
y2 = event.getY();
// update line
line.setEndX( x2 );
line.setEndY( y2 );
}
}
}
}
}
The first thing to know is that many JavaFX UI-Element like charts consist of many underlying children. The part where you want to draw is in fact a Region that is part of a Pane that is child of the LineChart. I really recommend you to use ScenicView, because it shows exactly how your scene graph (including built-in UI-Components) looks like.
Back to your problem: your listeners should only apply to the Region which shows the actual representation of the data. That Region ends exactly where the x and y axis are. The following code will get you that Region and make it the target for your listeners:
//your previous code in start()...
Pane p = (Pane) lineChart.getChildrenUnmodifiable().get(1);
Region r = (Region) p.getChildren().get(0);
LinesEdit.MouseHandler mh = new LinesEdit.MouseHandler(r);
r.setOnMouseClicked(mh);
r.setOnMouseMoved(mh);
stage.setScene(scene);
path = new Path();
path.setStrokeWidth(1);
path.setStroke(Color.BLACK);
r.setOnMouseDragged(mh);
r.setOnMousePressed(mh);
bp.getChildren().add(path);
stage.setScene(scene);
//the following code.....
The next steps are:
1. rewrite your handler methods, so that they accept a `Region` object as parameter
2. rewrite a bit of your line-setting code. Background: you cannot add objects into a `Region` because you cannot access the writable `List` of childrens. So you have to put the lines into the `Pane` object which holds the `Region`. Because every JavaFX UI-Element has its own coordinate system, you have to calculate the offset between the `Pane` and the `Region`, because the `Pane` is a bit larger. If you don't do this, your line will be drawn slightly above the mousepointer. You can get the width and height of a `Pane` and/or `Region` by calling `xyz.getBoundsInLocal().getWidth()/height()`.
UPDATE: Full Solution as requested
As requested in the comments i will show one way to solve this problem. Have a look at your GUI in this picture. It shows all graphical elements in ScenicView. As you can see, the Pane is bigger than the inner Region. Important for us is to know that the origin of all coordinate systems in JavaFX start at the upper left corner of an element. In this scenario, you have to add a line to the Pane, but in respect to the borders of the Region. In the code-snippet i showed earlier i added all of your listeners to the region, which means we get the mouse coordinates inside the coordinate system of the region. Now we have to "translate" or better "transform" these coordinates (the points you wish to set the start or endpoints of the line) into the coordinate system of the Pane (the place the line is actually placed, read above why), because we want the line to start exactly where our mouse is. There is a method you can call to get a transformation matrix: r.getLocalToParentTransform(). We need this matrix because we have to get the exact values for the x and y translation that is applied to the Region (you can see that the Region is approximatly 10 pixels moved from the Panes origin in both x and y axes)
I wrote a simple method for getting the x and y translation between the Region and the Pane: getCoordDiff(Region r, Pane p).
The rest of start() method remains unchanged (but with the changes i wrote earlier). But the handle() methods of MouseHandler and LineHandler have to be modified.
public class LinesEdit extends Application
{
Path path;
public double[] getCoordDiff(Region r, Pane p)
{
//Acquires transformation matrix and returns x and y offset/translation from parent
double[] diffs =
{ r.getLocalToParentTransform().getTx(), r.getLocalToParentTransform().getTy() };
return diffs;
}
public static void main(String[] args)
{
launch(args);
}
#Override
public void start(Stage stage)
{
final CategoryAxis xAxis = new CategoryAxis();
final NumberAxis yAxis = new NumberAxis(1, 21, 0.1);
yAxis.setTickUnit(1);
yAxis.setPrefWidth(35);
yAxis.setMinorTickCount(10);
yAxis.setSide(Side.RIGHT);
yAxis.setTickLabelFormatter(new NumberAxis.DefaultFormatter(yAxis)
{
#Override
public String toString(Number object)
{
String label;
label = String.format("%7.2f", object.floatValue());
return label;
}
});
final LineChart<String, Number> lineChart = new LineChart<String, Number>(xAxis, yAxis);
lineChart.setCreateSymbols(false);
lineChart.setAlternativeRowFillVisible(false);
lineChart.setLegendVisible(false);
XYChart.Series series1 = new XYChart.Series();
series1.getData().add(new XYChart.Data("Jan", 1));
series1.getData().add(new XYChart.Data("Feb", 4));
series1.getData().add(new XYChart.Data("Mar", 2.5));
series1.getData().add(new XYChart.Data("Apr", 5));
series1.getData().add(new XYChart.Data("May", 6));
series1.getData().add(new XYChart.Data("Jun", 8));
series1.getData().add(new XYChart.Data("Jul", 12));
series1.getData().add(new XYChart.Data("Aug", 8));
series1.getData().add(new XYChart.Data("Sep", 11));
series1.getData().add(new XYChart.Data("Oct", 13));
series1.getData().add(new XYChart.Data("Nov", 10));
series1.getData().add(new XYChart.Data("Dec", 20));
BorderPane bp = new BorderPane();
bp.setCenter(lineChart);
Scene scene = new Scene(bp, 800, 600);
lineChart.setAnimated(false);
lineChart.getData().addAll(series1);
Pane p = (Pane) lineChart.getChildrenUnmodifiable().get(1);
Region r = (Region) p.getChildren().get(0);
LinesEdit.MouseHandler mh = new LinesEdit.MouseHandler(r);
r.setOnMouseClicked(mh);
r.setOnMouseMoved(mh);
stage.setScene(scene);
path = new Path();
path.setStrokeWidth(1);
path.setStroke(Color.BLACK);
r.setOnMouseDragged(mh);
r.setOnMousePressed(mh);
bp.getChildren().add(path);
stage.setScene(scene);
ScenicView.show(scene);
stage.show();
}
class MouseHandler implements EventHandler<MouseEvent>
{
private boolean gotFirst = false;
private Line line;
private Region reg;
private double x1, y1, x2, y2;
private LineHandler lineHandler;
public MouseHandler(Region reg)
{
this.reg = reg;
lineHandler = new LineHandler(reg);
}
class LineHandler implements EventHandler<MouseEvent>
{
double x, y;
Region reg;
public LineHandler(Region reg)
{
this.reg = reg;
}
#Override
public void handle(MouseEvent e)
{
Line l = (Line) e.getSource();
l.setStrokeWidth(3);
// remove line on right click
if (e.getEventType() == MouseEvent.MOUSE_PRESSED && e.isSecondaryButtonDown())
{
((Pane) reg.getParent()).getChildren().remove(l);
}
else if (e.getEventType() == MouseEvent.MOUSE_DRAGGED && e.isPrimaryButtonDown())
{
double tx = e.getX();
double ty = e.getY();
double dx = tx - x;
double dy = ty - y;
l.setStartX(l.getStartX() + dx);
l.setStartY(l.getStartY() + dy);
l.setEndX(l.getEndX() + dx);
l.setEndY(l.getEndY() + dy);
x = tx;
y = ty;
}
else if (e.getEventType() == MouseEvent.MOUSE_ENTERED)
{
// just to show that the line is selected
x = e.getX();
y = e.getY();
l.setStroke(Color.RED);
}
else if (e.getEventType() == MouseEvent.MOUSE_EXITED)
{
l.setStroke(Color.BLACK);
}
// should not pass event to the parent
e.consume();
}
}
#Override
public void handle(MouseEvent event)
{
if (event.getEventType() == MouseEvent.MOUSE_CLICKED)
{
double[] diff = getCoordDiff(reg, (Pane) reg.getParent());
if (!gotFirst)
{
//add translation to start/endcoordinates
x1 = x2 = event.getX() + diff[0];
y1 = y2 = event.getY() + diff[1];
line = new Line(x1, y1, x2, y2);
line.setStrokeWidth(3);
((Pane) reg.getParent()).getChildren().add(line);
gotFirst = true;
line.setOnMouseClicked(new EventHandler<Event>()
{
#Override
public void handle(Event arg0)
{
line.setOnMouseEntered(lineHandler);
line.setOnMouseExited(lineHandler);
line.setOnMouseDragged(lineHandler);
line.setOnMousePressed(lineHandler);
// to consume the event
line.setOnMouseClicked(lineHandler);
line.setOnMouseReleased(lineHandler);
line = null;
gotFirst = false;
}
});
}
}
else
{
if (line != null)
{
double[] diff = getCoordDiff(reg, (Pane) reg.getParent());
//add translation to end coordinates
x2 = event.getX() + diff[0];
y2 = event.getY() + diff[1];
// update line
line.setEndX(x2);
line.setEndY(y2);
}
}
}
}
}
You can see the parts where i add the translation values to the lines start and endpoints. This is needed so that line really starts and ends at the points where your mouse is. I moved your code that was executed if gotFirst == true, because it prevented the user to place the line (so that it doesn't follow the cursor). Background: your cursor is now always (pixel perfect) at the end of the line , which doesn't have a 'Listener' at the moment you place it the first time. That missing listener prevents the MouseEvent-click from going to the Region. In short: the "Click" event is now always done on the line itself, thats why we need a listener before the line is finally placed.
Remaining bugs: a line cannot be placed on the yellow lines of the graph. Thats because the click-event is not triggerd on the lines. I might fix that bug at a later time, or you try yourself.

Rotate a group of Graphics objects around a single point?

I found an image online (http://i.stack.imgur.com/y1oT4.png) and I'm trying to take the sun and sky and make them rotate around the center of the screen, such that the sun and its rays appear to be spinning.
I intend to use a timer to control the movement, but I can't figure out how to rotate by an arbitrary angle. In other words, I know how to rotate by increments of 90 (switch the width and height), but what I'm trying to do is group a set of objects and rotate them around a single point.
I've looked around and found the AffineTransform() method, but I can't figure out if this is really what I need or how to use it if it is.
EDIT: Does this solve my problem? How to rotate Graphics in Java I will try it and update.
EDIT: It got me closer, but did not fix it. It returns this runtime error:
Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
at FallScene.rotateBack(FallScene.java:77)
at SceneDriver$1TimerListener.actionPerformed(SceneDriver.java:66)
at javax.swing.Timer.fireActionPerformed(Timer.java:312)
at javax.swing.Timer$DoPostEvent.run(Timer.java:244)
at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:251)
at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:705)
at java.awt.EventQueue.access$000(EventQueue.java:101)
at java.awt.EventQueue$3.run(EventQueue.java:666)
at java.awt.EventQueue$3.run(EventQueue.java:664)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDo
main.java:76)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:675)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThre
ad.java:211)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.
java:128)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThre
ad.java:117)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:113)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:105)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:90)
Press any key to continue...
The call at FallScene.rotateBack(FallScene.java:77) is:
bg.rotate(Math.toRadians(deg));
...which goes to:
public void paintComponent(Graphics g)
{
super.paintComponent(g);
// Get the size of the component window
int w = getWidth();
int h = getHeight();
// The Graphics2D object for the BACKGROUND
Graphics2D bg = (Graphics2D)g;
// Sun
Color solarYellow = new Color(255, 218, 0);
bg.setPaint(solarYellow);
Ellipse2D.Double sun = new Ellipse2D.Double((w / 2) - 150, (h / 2) - 150, 300, 300);
bg.fill(sun); bg.draw(sun);
}
If you still need it, I think this operational and commented code should help you understand how to draw it.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.awt.geom.Path2D;
import java.util.TimerTask;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class SunRotate extends JComponent
{
public static void main(String[] args) {
final SunRotate sunRotate = new SunRotate(45);
JFrame f = new JFrame();
f.setContentPane(sunRotate);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.pack();
f.setSize(new Dimension(800, 600));
f.setVisible(true);
new java.util.Timer().scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
sunRotate.deltaAngle(.3f);
sunRotate.repaint();
}
}, 16, 16); // every 16 milliseconds
}
private float angle;
public void deltaAngle(float delta) {
angle += delta;
}
public SunRotate(float angle) {
this.angle = angle;
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
int w = getWidth();
int h = getHeight();
// Recover Graphics2D
Graphics2D g2 = (Graphics2D) g;
// Move and rotate
g2.translate(w/2.0, h/2.0);
g2.rotate(Math.toRadians(angle));
// draw around 0,0
Color solarYellow = new Color(255, 218, 0);
g2.setPaint(solarYellow);
Ellipse2D.Double sun = new Ellipse2D.Double( -150, -150, 300, 300);
g2.fill(sun);
{ // draw some rays because the sun is round so we don't see the rotation
// make a ray (triangle)
Path2D ray = new Path2D.Float();
ray.moveTo(0, 0);
ray.lineTo(1000, 50);
ray.lineTo(1000, -50);
ray.closePath();
// draw N rays, rotating each time
int N = 20;
for (int i = 0; i < N; i++) {
g2.fill(ray);
g2.rotate(Math.PI * 2 / N);
}
}
}
}

JavaFX 2 circle path for animation

How can I create a circle (or ellipse) javafx.scene.shape.Path in JavaFX 2?
I've found some examples using CubicCurveTo:
Path path = new Path();
path.getElements().add(new CubicCurveTo(30, 10, 380, 120, 200, 120));
but I don't understand that Bézier coordinates. I need a full circle path for animations.
You can utilize the ArcTo path element to draw circle or ellipse path:
public class ArcToDemo extends Application {
private PathTransition pathTransitionEllipse;
private PathTransition pathTransitionCircle;
private void init(Stage primaryStage) {
Group root = new Group();
primaryStage.setResizable(false);
primaryStage.setScene(new Scene(root, 600, 460));
// Ellipse path example
Rectangle rect = new Rectangle(0, 0, 40, 40);
rect.setArcHeight(10);
rect.setArcWidth(10);
rect.setFill(Color.ORANGE);
root.getChildren().add(rect);
Path path = createEllipsePath(200, 200, 50, 100, 45);
root.getChildren().add(path);
pathTransitionEllipse = PathTransitionBuilder.create()
.duration(Duration.seconds(4))
.path(path)
.node(rect)
.orientation(OrientationType.ORTHOGONAL_TO_TANGENT)
.cycleCount(Timeline.INDEFINITE)
.autoReverse(false)
.build();
// Cirle path example
Rectangle rect2 = new Rectangle(0, 0, 20, 20);
rect2.setArcHeight(10);
rect2.setArcWidth(10);
rect2.setFill(Color.GREEN);
root.getChildren().add(rect2);
Path path2 = createEllipsePath(400, 200, 150, 150, 0);
root.getChildren().add(path2);
pathTransitionCircle = PathTransitionBuilder.create()
.duration(Duration.seconds(2))
.path(path2)
.node(rect2)
.orientation(OrientationType.ORTHOGONAL_TO_TANGENT)
.cycleCount(Timeline.INDEFINITE)
.autoReverse(false)
.build();
}
private Path createEllipsePath(double centerX, double centerY, double radiusX, double radiusY, double rotate) {
ArcTo arcTo = new ArcTo();
arcTo.setX(centerX - radiusX + 1); // to simulate a full 360 degree celcius circle.
arcTo.setY(centerY - radiusY);
arcTo.setSweepFlag(false);
arcTo.setLargeArcFlag(true);
arcTo.setRadiusX(radiusX);
arcTo.setRadiusY(radiusY);
arcTo.setXAxisRotation(rotate);
Path path = PathBuilder.create()
.elements(
new MoveTo(centerX - radiusX, centerY - radiusY),
arcTo,
new ClosePath()) // close 1 px gap.
.build();
path.setStroke(Color.DODGERBLUE);
path.getStrokeDashArray().setAll(5d, 5d);
return path;
}
#Override
public void start(Stage primaryStage) throws Exception {
init(primaryStage);
primaryStage.show();
pathTransitionEllipse.play();
pathTransitionCircle.play();
}
public static void main(String[] args) {
launch(args);
}
}
Good reference of the features of ArcTo is ArcTo (JavaFX 8). Allthough it is version 8, the meanings of the features are similar.
Output:
This is an updated version of #Uluk Biy's answer.
import javafx.animation.PathTransition;
import javafx.animation.PathTransition.OrientationType;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.paint.Color;
import javafx.scene.shape.ArcTo;
import javafx.scene.shape.ClosePath;
import javafx.scene.shape.MoveTo;
import javafx.scene.shape.Path;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;
public class PathTDemo extends Application
{
private PathTransition pathTransitionEllipse;
private PathTransition pathTransitionCircle;
private void init(Stage primaryStage)
{
Group root = new Group();
primaryStage.setResizable(false);
primaryStage.setScene(new Scene(root, 600, 460));
// Ellipse path example
Rectangle rect = new Rectangle(0, 0, 40, 40);
rect.setArcHeight(10);
rect.setArcWidth(10);
rect.setFill(Color.ORANGE);
root.getChildren().add(rect);
Path path = createEllipsePath(200, 200, 50, 100, 45);
root.getChildren().add(path);
pathTransitionEllipse = new PathTransition();
pathTransitionEllipse.setDuration(Duration.seconds(4));
pathTransitionEllipse.setPath(path);
pathTransitionEllipse.setNode(rect);
pathTransitionEllipse.setOrientation(OrientationType.ORTHOGONAL_TO_TANGENT);
pathTransitionEllipse.setCycleCount(Timeline.INDEFINITE);
pathTransitionEllipse.setAutoReverse(false);
// Cirle path example
Rectangle rect2 = new Rectangle(0, 0, 20, 20);
rect2.setArcHeight(10);
rect2.setArcWidth(10);
rect2.setFill(Color.GREEN);
root.getChildren().add(rect2);
Path path2 = createEllipsePath(400, 200, 150, 150, 0);
root.getChildren().add(path2);
pathTransitionCircle = new PathTransition();
pathTransitionCircle.setDuration(Duration.seconds(2));
pathTransitionCircle.setPath(path2);
pathTransitionCircle.setNode(rect2);
pathTransitionCircle.setOrientation(OrientationType.ORTHOGONAL_TO_TANGENT);
pathTransitionCircle.setCycleCount(Timeline.INDEFINITE);
pathTransitionCircle.setAutoReverse(false);
}
private Path createEllipsePath(double centerX, double centerY, double radiusX, double radiusY, double rotate)
{
ArcTo arcTo = new ArcTo();
arcTo.setX(centerX - radiusX + 1); // to simulate a full 360 degree celcius circle.
arcTo.setY(centerY - radiusY);
arcTo.setSweepFlag(false);
arcTo.setLargeArcFlag(true);
arcTo.setRadiusX(radiusX);
arcTo.setRadiusY(radiusY);
arcTo.setXAxisRotation(rotate);
Path path = new Path();
path.getElements().addAll(
new MoveTo(centerX - radiusX, centerY - radiusY),
arcTo,
new ClosePath()); // close 1 px gap.
path.setStroke(Color.DODGERBLUE);
path.getStrokeDashArray().setAll(5d, 5d);
return path;
}
#Override
public void start(Stage primaryStage) throws Exception
{
init(primaryStage);
primaryStage.show();
pathTransitionEllipse.play();
pathTransitionCircle.play();
}
public static void main(String[] args)
{
launch(args);
}
}
I solved same problem by animation of rotateProperty of container. Just two lines for creating animation.
animationTimeLine = new Timeline(60, new KeyFrame(Duration.seconds(5), new KeyValue(circlePane.rotateProperty(), 360.0)));
animationTimeLine.setCycleCount(INDEFINITE);

Zoom multiple sprites, individually on touch-Andengine

I am very new to andEngine, I want to add sprites on screen and let them move and zoom on finger touch.
Right now i am able to add multiple sprites on scene , and they can be dragged on touch.
Here is my code:
public class Main extends SimpleBaseGameActivity {
#Override
private Camera camera;
private BitmapTextureAtlas mBitmapTextureAtlas;
private ITextureRegion mFaceTextureRegion;
private ITextureRegion mFaceTextureRegion2;
private static final int CAMERA_WIDTH = 800;
private static final int CAMERA_HEIGHT = 480;
#Override
public EngineOptions onCreateEngineOptions() {
camera = new Camera(0, 0, CAMERA_WIDTH, CAMERA_HEIGHT);
EngineOptions engineOptions = new EngineOptions(true,
ScreenOrientation.LANDSCAPE_FIXED, new RatioResolutionPolicy(
CAMERA_WIDTH, CAMERA_HEIGHT), camera);
return engineOptions;
}
#Override
protected void onCreateResources() {
BitmapTextureAtlasTextureRegionFactory.setAssetBasePath("gfx/");
this.mBitmapTextureAtlas = new BitmapTextureAtlas(
this.getTextureManager(), 1024, 1600, TextureOptions.NEAREST);
// background
// this.background = new Sprite(0, 0,
// BitmapTextureAtlasTextureRegionFactory.createTiledFromAsset(this.mBitmapTextureAtlas,
// this, "ui_ball_1.png", 0, 0, 1, 1),
// this.getVertexBufferObjectManager());
this.mFaceTextureRegion = BitmapTextureAtlasTextureRegionFactory
.createFromAsset(this.mBitmapTextureAtlas, this,
"ui_ball_1.png", 0, 0);
this.mFaceTextureRegion2 = BitmapTextureAtlasTextureRegionFactory
.createFromAsset(this.mBitmapTextureAtlas, this,
"ui_ball_1.png", 0, 0);
this.mBitmapTextureAtlas.load();
// this.mEngine.getTextureManager().loadTexture(this.mBitmapTextureAtlas);
/*
* this.mBitmapTextureAtlas = new
* BitmapTextureAtlas(this.getTextureManager(), 32, 32,
* TextureOptions.BILINEAR); this.mFaceTextureRegion =
* BitmapTextureAtlasTextureRegionFactory
* .createFromAsset(this.mBitmapTextureAtlas, this, "ui_ball_1.png", 0,
* 0); this.mBitmapTextureAtlas.load();
*/
}
#Override
protected Scene onCreateScene() {
/*
* this.scene = new Scene(); this.scene.attachChild(this.background);
* this.scene.setBackground(new Background(0.09804f, 0.6274f, 0.8784f));
* return this.scene;
*/
this.mEngine.registerUpdateHandler(new FPSLogger());
final Scene scene = new Scene();
scene.setBackground(new Background(0.09804f, 0.6274f, 0.8784f));
final float centerX = (CAMERA_WIDTH - this.mFaceTextureRegion
.getWidth()) / 2;
final float centerY = (CAMERA_HEIGHT - this.mFaceTextureRegion
.getHeight()) / 2;
final Sprite face = new Sprite(centerX, centerY,
this.mFaceTextureRegion, this.getVertexBufferObjectManager()) {
#Override
public boolean onAreaTouched(final TouchEvent pSceneTouchEvent,
final float pTouchAreaLocalX, final float pTouchAreaLocalY) {
this.setPosition(pSceneTouchEvent.getX() - this.getWidth() / 2,
pSceneTouchEvent.getY() - this.getHeight() / 2);
return true;
}
};
face.setScale(2);
scene.attachChild(face);
scene.registerTouchArea(face);
final Sprite face2 = new Sprite(0, 0, this.mFaceTextureRegion2,
this.getVertexBufferObjectManager()) {
#Override
public boolean onAreaTouched(final TouchEvent pSceneTouchEvent,
final float pTouchAreaLocalX, final float pTouchAreaLocalY) {
this.setPosition(pSceneTouchEvent.getX() - this.getWidth() / 2,
pSceneTouchEvent.getY() - this.getHeight() / 2);
return true;
}
};
face2.setScale(2);
scene.attachChild(face2);
scene.registerTouchArea(face2);
scene.setTouchAreaBindingOnActionDownEnabled(true);
return scene;
}
}
Now i want to zoom each sprite on touch, but unable to find such method like setPosition available to move sprite to a specific position. Can anyone help me in achieving this without affecting the current functionality. Any help would be appreciated, may be in form of code or some direction/method to do this.
Thanks in advance :)
you can use a EntityModifier to make your effect:
#Override
public boolean onAreaTouched(final TouchEvent pSceneTouchEvent,
final float pTouchAreaLocalX, final float pTouchAreaLocalY) {
this.setPosition(pSceneTouchEvent.getX() - this.getWidth() / 2,
pSceneTouchEvent.getY() - this.getHeight() / 2);
this.clearEntityModifiers();
this.RegisterEntityModifier(new ScaleModifier(1f,2f,4f));
//1f = time to convert the scale of sprite of 2f to 4f
//2f = initial scale
//4f = finish scale
//when you dont touch the sprite back to normal scale
if(event.getAction()== MotionEvent.ACTION_UP) {
this.clearEntityModifiers();
this.RegisterEntityModifier(new ScaleModifier(1f,4f,2f));
}
//you also can work with "MotionEvent.ACTION_DOWN" and
//MotionEvent.ACTION_MOVE
return true;
}

Resources