JavaFX: Make dragged node visible - drag-and-drop

I want to drag and drop nodes between flowpanes. I implemented the drag and drop in this way:
public class TouchTask extends BorderPane{
setOnDragDetected(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
Dragboard dragboard = startDragAndDrop(TransferMode.ANY);
ClipboardContent clipboardContent = new ClipboardContent();
clipboardContent.putString(TASK_DRAG_KEY);
dragboard.setContent(clipboardContent);
event.consume();
}
});
}
The drag and drop works fine, but the problem is, that the node is not moved during the drag and drop gesture. I wanted to implement this drag and drop so that the node has the same position as the mouse during the gesture.
I tried to implement this in the following way:
onMousePressedProperty().set(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
// record the current mouse X and Y position on Node
mousex = event.getSceneX();
mousey = event.getSceneY();
x = getLayoutX();
y = getLayoutY();
if (isMoveToFront()) {
toFront();
}
}
});
onMouseDraggedProperty().set(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
// Get the exact moved X and Y
double offsetX = event.getSceneX() - mousex;
double offsetY = event.getSceneY() - mousey;
x += offsetX;
y += offsetY;
double scaledX = x;
double scaledY = y;
setLayoutX(scaledX);
setLayoutY(scaledY);
// again set current Mouse x AND y position
mousex = event.getSceneX();
mousey = event.getSceneY();
event.consume();
}
});
But with this solution, the node is only moved for like 3 pixel and then it stops.

Related

How to smothly move the character controller between x positions like in Subway Surfer in Unity?

I'm trying to replicate the movement from Subway Surfers in Unity but I can't achieve that.
The character is located in the position 0 in the x-axis and when the left button is pressed the player will subtract 3 from the current position, and if the right button is pressed the player will add 3 to the current position making him move between -3,0,3 in the x-axis and all of this keeping the y and z axes unchanged.
I've tried using both Lerp and Slerp but I just couldn't achieve what I wanted. I've come down to a broken code that also changes the y, z-axis what I don't want to do:
var currPos = transform.position;
var currPosX = currPos.x;
if (Input.GetKeyUp(KeyCode.A))
{
if (currPosX > -horizontalSteps)
{
var newPos = new Vector3(currPosX - horizontalSteps, currPos.y, currPos.z);
newPos = Vector3.Slerp(currPos, newPos, 3);
Controller.Move(newPos);
}
}
if (Input.GetKeyUp(KeyCode.D))
{
if (currPosX < horizontalSteps)
{
var newPos = new Vector3(currPosX + horizontalSteps, currPos.y, currPos.z);
newPos = Vector3.Slerp(currPos, newPos, 3);
Controller.Move(newPos);
}
}
there is a very simple way
the character have 3 states so let's say we have a state variable (-1 , 0 , 1) for left, mid, and right
, and init with 0
if the player press right state++, if press left state--
completed if statement
if(button.right && state < 1) state++; "move right script";
else if(button.left && state > -1) state-- "move left script";
well,
about moving
I prefer Vector3.MoveTowards()for smoothly moving and avoid issues
try this example
using UnityEngine;
using UnityEngine.UI;
public class movv : MonoBehaviour
{
public Button right;
public Button left;
public float speed;
private int state = 0;
void Start()
{
Button btn = right.GetComponent<Button>();
btn.onClick.AddListener(delegate { moveright(); });
Button btn2 = left.GetComponent<Button>();
btn2.onClick.AddListener(delegate { moveleft(); });
void moveright()
{
if (state < 1) state++;
}
void moveleft()
{
if(state > -1) state--;
}
}
void Update()
{
Vector3 x = new Vector3 (state * 3, transform.position.y, transform.position.z);
transform.position = Vector3.MoveTowards(transform.position, x, speed * Time.deltaTime);
}
}
that's a quick one...
Hint:
this code works when the character position on the x-axis equal to 0
if not,
this is a general example,
using UnityEngine;
using UnityEngine.UI;
public class movv : MonoBehaviour
{
public Button right;
public Button left;
public float speed;
private Vector3 x;
private int state = 0;
private int target = 0;
void Start()
{
x = transform.position;
Button btn = right.GetComponent<Button>();
btn.onClick.AddListener(delegate { moveright(); });
Button btn2 = left.GetComponent<Button>();
btn2.onClick.AddListener(delegate { moveleft(); });
void moveright()
{
if (state < 1)
{
target = 1;
x = new Vector3(transform.position.x + target * 3, transform.position.y, transform.position.z);
state++;
}
}
void moveleft()
{
if (state > -1)
{
target = -1;
x = new Vector3(transform.position.x + target * 3, transform.position.y, transform.position.z);
state--;
}
}
}
void Update()
{
transform.position = Vector3.MoveTowards(transform.position, x, speed * Time.deltaTime);
}
}

JavaFX AnimationTimer seems out of sync, untill window is resized

A little introduction: I've created a simple (for now) application which uses an AnimationTimer to update animations and draw objects to the Canvas. Everything runs smoothly and the timer adjusts its fps to the refresh-rate of my laptop (50/60Hz).
When I start the program however, there seems to be something wrong which causes my animations to appear 'jurky' or dropping frames, but the framerate stays a solid 60/50fps. Then when I resize the window for the first time (no difference how many), suddenly all the animations are super-smooth. After that, everything stays 'synced' no matter the resizes done.
What is the reason that the AnimationTimerstarts 'out-of-sync' until the window is resized and can it be prevented?
Update
Added a code example. The problem is mostly visible when on 50Hz, but also exist on 60Hz. Using Eclipse on Windows 10 (first code-share, may be to much/missing things).
public void start(Stage primaryStage) {
try {
pane = new Pane();
drawables = new ArrayList<>();
canvas = new Canvas(400,400);
canvas.widthProperty().bind(pane.widthProperty());
canvas.heightProperty().bind(pane.heightProperty());
GraphicsContext g = canvas.getGraphicsContext2D();
SimpleAnimatedCircle circle = new SimpleAnimatedCircle(20);
circle.setX(100);
circle.setY(50);
timer = new AnimationTimer() {
#Override
public void handle(long now) {
frameCount++;
if (System.currentTimeMillis() > frameStart + 500) {
System.out.println("FPS: " + frameCount*2);
frameStart = System.currentTimeMillis();
frameCount = 0;
}
for (Drawable drawable:drawables) {
drawable.update();
}
g.setFill(Color.DARKSLATEBLUE);
g.fillRect(0, 0, canvas.getWidth(), canvas.getHeight());
circle.draw(g);
}
};
timer.start();
canvas.setOnMouseClicked((e) -> {
circle.start();
});
pane.getChildren().add(canvas);
Scene scene = new Scene(pane,400,400);
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static class SimpleAnimatedCircle {
double diameter;
double x;
double y;
long startTime;
double diffY = 300; // Animated distance over y-axis.
double duration = 2000; // 2 second duration.
public SimpleAnimatedCircle(double diameter) {
this.diameter = diameter;
}
public void setX(double value) {
x = value;
}
public void setY(double value) {
y = value;
}
public void start() {
startTime = System.currentTimeMillis();
}
public void draw(GraphicsContext g) {
double animatedY = y;
// Update the animation.
if (System.currentTimeMillis() < startTime + duration) {
animatedY = y + (System.currentTimeMillis() - startTime) /
duration * diffY;
}
g.setFill(Color.ORANGE);
g.fillOval(x, animatedY, diameter, diameter);
}
}

Sprite long click in andengine

I am using an animated sprite and want to perform some action on its long click events, but it is not working. Please help guys...
AnimatedSprite playBtn = new AnimatedSprite(MainActivity.CAMERA_WIDTH/2, MainActivity.CAMERA_HEIGHT - 100, activity.tTextureRegion3, activity.getVertexBufferObjectManager())
{
#Override
public boolean onAreaTouched(TouchEvent pSceneTouchEvent, float X, float Y)
{
if (pSceneTouchEvent.isActionDown())
{
scoreInc = scoreInc + 10;
score.setText("Score: " + scoreInc);
width = width + 5;
progressRectangle();
return true;
}
return false;
};
};
this.registerTouchArea(playBtn);
this.setTouchAreaBindingOnActionDownEnabled(true);
I have used this
#Override
public boolean onAreaTouched(TouchEvent pSceneTouchEvent, float X, float Y)
{
switch(pSceneTouchEvent.getAction()) {
case TouchEvent.ACTION_MOVE:{ //Done my Work}
and it worked

JavaFX 8 Dynamic Node scaling

I'm trying to implement a scene with a ScrollPane in which the user can drag a node around and scale it dynamically. I have the dragging and scaling with the mouse wheel working as well as a reset zoom, but I'm having trouble with the calculations to fit the node to the width of the parent.
Here is my code as an sscce.
(works) Mouse wheel will zoom in and out around the mouse pointer
(works) Left or right mouse press to drag the rectangle around
(works) Left double-click to reset the zoom
(doesn't work) Right double-click to fit the width
If I zoom in or out or change the window size, the fit to width does not work.
If anyone can help me with the calculations to fit the node to the width of the parent, I would very much appreciate it.
EDITED:
I marked the method that is not working correctly. It is fitWidth(), which is invoked by right mouse button double-clicking.
I edited the text of the question for clarity and focus
Hopefully this is more clear now.
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.ScrollPane.ScrollBarPolicy;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.ScrollEvent;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.shape.StrokeType;
import javafx.stage.Stage;
import javafx.util.Duration;
public class ZoomAndPanExample extends Application {
private ScrollPane scrollPane = new ScrollPane();
private final DoubleProperty zoomProperty = new SimpleDoubleProperty(1.0d);
private final DoubleProperty deltaY = new SimpleDoubleProperty(0.0d);
private final Group group = new Group();
public static void main(String[] args) {
Application.launch(args);
}
#Override
public void start(Stage primaryStage) {
scrollPane.setPannable(true);
scrollPane.setHbarPolicy(ScrollBarPolicy.NEVER);
scrollPane.setVbarPolicy(ScrollBarPolicy.NEVER);
AnchorPane.setTopAnchor(scrollPane, 10.0d);
AnchorPane.setRightAnchor(scrollPane, 10.0d);
AnchorPane.setBottomAnchor(scrollPane, 10.0d);
AnchorPane.setLeftAnchor(scrollPane, 10.0d);
AnchorPane root = new AnchorPane();
Rectangle rect = new Rectangle(80, 60);
rect.setStroke(Color.NAVY);
rect.setFill(Color.NAVY);
rect.setStrokeType(StrokeType.INSIDE);
group.getChildren().add(rect);
// create canvas
PanAndZoomPane panAndZoomPane = new PanAndZoomPane();
zoomProperty.bind(panAndZoomPane.myScale);
deltaY.bind(panAndZoomPane.deltaY);
panAndZoomPane.getChildren().add(group);
SceneGestures sceneGestures = new SceneGestures(panAndZoomPane);
scrollPane.setContent(panAndZoomPane);
panAndZoomPane.toBack();
scrollPane.addEventFilter( MouseEvent.MOUSE_CLICKED, sceneGestures.getOnMouseClickedEventHandler());
scrollPane.addEventFilter( MouseEvent.MOUSE_PRESSED, sceneGestures.getOnMousePressedEventHandler());
scrollPane.addEventFilter( MouseEvent.MOUSE_DRAGGED, sceneGestures.getOnMouseDraggedEventHandler());
scrollPane.addEventFilter( ScrollEvent.ANY, sceneGestures.getOnScrollEventHandler());
root.getChildren().add(scrollPane);
Scene scene = new Scene(root, 600, 400);
primaryStage.setScene(scene);
primaryStage.show();
}
class PanAndZoomPane extends Pane {
public static final double DEFAULT_DELTA = 1.3d;
DoubleProperty myScale = new SimpleDoubleProperty(1.0);
public DoubleProperty deltaY = new SimpleDoubleProperty(0.0);
private Timeline timeline;
public PanAndZoomPane() {
this.timeline = new Timeline(60);
// add scale transform
scaleXProperty().bind(myScale);
scaleYProperty().bind(myScale);
}
public double getScale() {
return myScale.get();
}
public void setScale( double scale) {
myScale.set(scale);
}
public void setPivot( double x, double y, double scale) {
// note: pivot value must be untransformed, i. e. without scaling
// timeline that scales and moves the node
timeline.getKeyFrames().clear();
timeline.getKeyFrames().addAll(
new KeyFrame(Duration.millis(200), new KeyValue(translateXProperty(), getTranslateX() - x)),
new KeyFrame(Duration.millis(200), new KeyValue(translateYProperty(), getTranslateY() - y)),
new KeyFrame(Duration.millis(200), new KeyValue(myScale, scale))
);
timeline.play();
}
/**
* !!!! The problem is in this method !!!!
*
* The calculations are incorrect, and result in unpredictable behavior
*
*/
public void fitWidth () {
double scale = getParent().getLayoutBounds().getMaxX()/getLayoutBounds().getMaxX();
double oldScale = getScale();
double f = (scale / oldScale)-1;
double dx = getTranslateX() - getBoundsInParent().getMinX() - getBoundsInParent().getWidth()/2;
double dy = getTranslateY() - getBoundsInParent().getMinY() - getBoundsInParent().getHeight()/2;
double newX = f*dx + getBoundsInParent().getMinX();
double newY = f*dy + getBoundsInParent().getMinY();
setPivot(newX, newY, scale);
}
public void resetZoom () {
double scale = 1.0d;
double x = getTranslateX();
double y = getTranslateY();
setPivot(x, y, scale);
}
public double getDeltaY() {
return deltaY.get();
}
public void setDeltaY( double dY) {
deltaY.set(dY);
}
}
/**
* Mouse drag context used for scene and nodes.
*/
class DragContext {
double mouseAnchorX;
double mouseAnchorY;
double translateAnchorX;
double translateAnchorY;
}
/**
* Listeners for making the scene's canvas draggable and zoomable
*/
public class SceneGestures {
private DragContext sceneDragContext = new DragContext();
PanAndZoomPane panAndZoomPane;
public SceneGestures( PanAndZoomPane canvas) {
this.panAndZoomPane = canvas;
}
public EventHandler<MouseEvent> getOnMouseClickedEventHandler() {
return onMouseClickedEventHandler;
}
public EventHandler<MouseEvent> getOnMousePressedEventHandler() {
return onMousePressedEventHandler;
}
public EventHandler<MouseEvent> getOnMouseDraggedEventHandler() {
return onMouseDraggedEventHandler;
}
public EventHandler<ScrollEvent> getOnScrollEventHandler() {
return onScrollEventHandler;
}
private EventHandler<MouseEvent> onMousePressedEventHandler = new EventHandler<MouseEvent>() {
public void handle(MouseEvent event) {
sceneDragContext.mouseAnchorX = event.getX();
sceneDragContext.mouseAnchorY = event.getY();
sceneDragContext.translateAnchorX = panAndZoomPane.getTranslateX();
sceneDragContext.translateAnchorY = panAndZoomPane.getTranslateY();
}
};
private EventHandler<MouseEvent> onMouseDraggedEventHandler = new EventHandler<MouseEvent>() {
public void handle(MouseEvent event) {
panAndZoomPane.setTranslateX(sceneDragContext.translateAnchorX + event.getX() - sceneDragContext.mouseAnchorX);
panAndZoomPane.setTranslateY(sceneDragContext.translateAnchorY + event.getY() - sceneDragContext.mouseAnchorY);
event.consume();
}
};
/**
* Mouse wheel handler: zoom to pivot point
*/
private EventHandler<ScrollEvent> onScrollEventHandler = new EventHandler<ScrollEvent>() {
#Override
public void handle(ScrollEvent event) {
double delta = PanAndZoomPane.DEFAULT_DELTA;
double scale = panAndZoomPane.getScale(); // currently we only use Y, same value is used for X
double oldScale = scale;
panAndZoomPane.setDeltaY(event.getDeltaY());
if (panAndZoomPane.deltaY.get() < 0) {
scale /= delta;
} else {
scale *= delta;
}
double f = (scale / oldScale)-1;
double dx = (event.getX() - (panAndZoomPane.getBoundsInParent().getWidth()/2 + panAndZoomPane.getBoundsInParent().getMinX()));
double dy = (event.getY() - (panAndZoomPane.getBoundsInParent().getHeight()/2 + panAndZoomPane.getBoundsInParent().getMinY()));
panAndZoomPane.setPivot(f*dx, f*dy, scale);
event.consume();
}
};
/**
* Mouse click handler
*/
private EventHandler<MouseEvent> onMouseClickedEventHandler = new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
if (event.getButton().equals(MouseButton.PRIMARY)) {
if (event.getClickCount() == 2) {
panAndZoomPane.resetZoom();
}
}
if (event.getButton().equals(MouseButton.SECONDARY)) {
if (event.getClickCount() == 2) {
panAndZoomPane.fitWidth();
}
}
}
};
}
}
I found the answer. I was looking at the wrong calculations, assuming it to be related to the translations. The real culprit was the calculation for the difference in scale. I simply changed this:
double f = (scale / oldScale)-1;
to this:
double f = scale - oldScale;

Mouse position relative to origin

How do I calculate the position of the mouse relative to the origin of a line chart?
The code below gets the x-position of the mouse cursor relative to the layout origin of the chart. I'd like to know the mouse position relative to the Cartesian origin instead.
public class Chart extends Application {
private NumberAxis xAxis;
private NumberAxis yAxis;
private LineChart<Number,Number> lineChart;
private Label cursorPosition;
private Label xAxisPosition;
#Override
public void start(Stage primaryStage) {
VBox root = new VBox();
xAxis = new NumberAxis("Date", 0.f, 100.f, 10.f);
yAxis = new NumberAxis("Value", 0.f, 100.f, 10.f);
lineChart = new LineChart<>(xAxis, yAxis);
Series series = new Series();
for (int ii = 1; ii <= 100; ii++) {
series.getData().add(new Data(ii, Math.random()*20.));
}
lineChart.getData().add(series);
lineChart.setOnMouseMoved(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
lineChart.setCursor(Cursor.CROSSHAIR);
cursorPosition.setText(String.valueOf(event.getX()));
}
});
lineChart.setOnMouseExited(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
lineChart.setCursor(Cursor.DEFAULT);
}
});
cursorPosition = new Label();
root.getChildren().addAll(lineChart, cursorPosition,);
Scene scene = new Scene(root, 300, 250);
primaryStage.setScene(scene);
primaryStage.show();
}
You'll have to use the chart's axes to convert between display coordinates and cartesian (value) coordinates. xAxis.getDisplayPosition(0) would, for instance, give you the x coordinate for the value 0. You can also use this to convert the current mouse position into 'values'.