JavaFx zooming to mouse as pivot - javafx

I have tried the this example given in another post to learn about zooming and panning relative to the mouse pointer. When everything is on the grid, zooming works as expected:
When zooming into the mouse pointer location on the top left image, it is zoomed into the exact location as seen in the top right image.
If something is dragged off the grid, e.g. the pivot starts to 'misbehave':
When zooming into the mouse pointer location on the bottom left image, it is zoomed into a location other than the one intended, seen in the bottom right image.
The bounds of the canvas inside the parent changes from 600x600 (without scale) to something like 600x700… Which affects the outcomes dx, dy of the following function.
double dx = (event.getSceneX() - (canvas.getBoundsInParent().getWidth()/2 + canvas.getBoundsInParent().getMinX()));
double dy = (event.getSceneY() - (canvas.getBoundsInParent().getHeight()/2 + canvas.getBoundsInParent().getMinY()));
When editing this function by changing .getWidth() to .getHeight() and then again move the rectangle out right… the zoom works correctly. However, if the rectangle is moved out vertically (to the bottom or top) and to the left the problem again is reproduced again.
Is the above function correct, what is it trying to do? Why does the zoom not work the same, as when everything was on the grid?
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.Node;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.control.Label;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.ScrollEvent;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
class PannableCanvas extends Pane {
DoubleProperty myScale = new SimpleDoubleProperty(1.0);
public PannableCanvas() {
setPrefSize(600, 600);
setStyle("-fx-background-color: lightgrey; -fx-border-color: blue;");
// add scale transform
scaleXProperty().bind(myScale);
scaleYProperty().bind(myScale);
}
/**
* Add a grid to the canvas, send it to back
*/
public void addGrid() {
double w = getBoundsInLocal().getWidth();
double h = getBoundsInLocal().getHeight();
// add grid
Canvas grid = new Canvas(w, h);
// don't catch mouse events
grid.setMouseTransparent(true);
GraphicsContext gc = grid.getGraphicsContext2D();
gc.setStroke(Color.GRAY);
gc.setLineWidth(1);
// draw grid lines
double offset = 50;
for( double i=offset; i < w; i+=offset) {
gc.strokeLine( i, 0, i, h);
gc.strokeLine( 0, i, w, i);
}
getChildren().add( grid);
grid.toBack();
}
public double getScale() {
return myScale.get();
}
public void setScale( double scale) {
myScale.set(scale);
}
public void setPivot( double x, double y) {
setTranslateX(getTranslateX()-x);
setTranslateY(getTranslateY()-y);
}
}
/**
* Mouse drag context used for scene and nodes.
*/
class DragContext {
double mouseAnchorX;
double mouseAnchorY;
double translateAnchorX;
double translateAnchorY;
}
/**
* Listeners for making the nodes draggable via left mouse button. Considers if parent is zoomed.
*/
class NodeGestures {
private DragContext nodeDragContext = new DragContext();
PannableCanvas canvas;
public NodeGestures( PannableCanvas canvas) {
this.canvas = canvas;
}
public EventHandler<MouseEvent> getOnMousePressedEventHandler() {
return onMousePressedEventHandler;
}
public EventHandler<MouseEvent> getOnMouseDraggedEventHandler() {
return onMouseDraggedEventHandler;
}
private EventHandler<MouseEvent> onMousePressedEventHandler = new EventHandler<MouseEvent>() {
public void handle(MouseEvent event) {
// left mouse button => dragging
if( !event.isPrimaryButtonDown())
return;
nodeDragContext.mouseAnchorX = event.getSceneX();
nodeDragContext.mouseAnchorY = event.getSceneY();
Node node = (Node) event.getSource();
nodeDragContext.translateAnchorX = node.getTranslateX();
nodeDragContext.translateAnchorY = node.getTranslateY();
}
};
private EventHandler<MouseEvent> onMouseDraggedEventHandler = new EventHandler<MouseEvent>() {
public void handle(MouseEvent event) {
// left mouse button => dragging
if( !event.isPrimaryButtonDown())
return;
double scale = canvas.getScale();
Node node = (Node) event.getSource();
node.setTranslateX(nodeDragContext.translateAnchorX + (( event.getSceneX() - nodeDragContext.mouseAnchorX) / scale));
node.setTranslateY(nodeDragContext.translateAnchorY + (( event.getSceneY() - nodeDragContext.mouseAnchorY) / scale));
event.consume();
}
};
}
/**
* Listeners for making the scene's canvas draggable and zoomable
*/
class SceneGestures {
private static final double MAX_SCALE = 10.0d;
private static final double MIN_SCALE = .1d;
private DragContext sceneDragContext = new DragContext();
PannableCanvas canvas;
public SceneGestures( PannableCanvas canvas) {
this.canvas = canvas;
}
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) {
// right mouse button => panning
if( !event.isSecondaryButtonDown())
return;
sceneDragContext.mouseAnchorX = event.getSceneX();
sceneDragContext.mouseAnchorY = event.getSceneY();
sceneDragContext.translateAnchorX = canvas.getTranslateX();
sceneDragContext.translateAnchorY = canvas.getTranslateY();
}
};
private EventHandler<MouseEvent> onMouseDraggedEventHandler = new EventHandler<MouseEvent>() {
public void handle(MouseEvent event) {
// right mouse button => panning
if( !event.isSecondaryButtonDown())
return;
canvas.setTranslateX(sceneDragContext.translateAnchorX + event.getSceneX() - sceneDragContext.mouseAnchorX);
canvas.setTranslateY(sceneDragContext.translateAnchorY + event.getSceneY() - 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 = 1.2;
double scale = canvas.getScale(); // currently we only use Y, same value is used for X
double oldScale = scale;
if (event.getDeltaY() < 0)
scale /= delta;
else
scale *= delta;
scale = clamp( scale, MIN_SCALE, MAX_SCALE);
double f = (scale / oldScale)-1;
double dx = (event.getSceneX() - (canvas.getBoundsInParent().getWidth()/2 + canvas.getBoundsInParent().getMinX()));
double dy = (event.getSceneY() - (canvas.getBoundsInParent().getHeight()/2 + canvas.getBoundsInParent().getMinY()));
canvas.setScale( scale);
// note: pivot value must be untransformed, i. e. without scaling
canvas.setPivot(f*dx, f*dy);
event.consume();
}
};
public static double clamp( double value, double min, double max) {
if( Double.compare(value, min) < 0)
return min;
if( Double.compare(value, max) > 0)
return max;
return value;
}
}
/**
* An application with a zoomable and pannable canvas.
*/
public class ZoomAndScrollApplication extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) {
Group group = new Group();
// create canvas
PannableCanvas canvas = new PannableCanvas();
// we don't want the canvas on the top/left in this example => just
// translate it a bit
canvas.setTranslateX(100);
canvas.setTranslateY(100);
// create sample nodes which can be dragged
NodeGestures nodeGestures = new NodeGestures( canvas);
Label label1 = new Label("Draggable node 1");
label1.setTranslateX(10);
label1.setTranslateY(10);
label1.addEventFilter( MouseEvent.MOUSE_PRESSED, nodeGestures.getOnMousePressedEventHandler());
label1.addEventFilter( MouseEvent.MOUSE_DRAGGED, nodeGestures.getOnMouseDraggedEventHandler());
Label label2 = new Label("Draggable node 2");
label2.setTranslateX(100);
label2.setTranslateY(100);
label2.addEventFilter( MouseEvent.MOUSE_PRESSED, nodeGestures.getOnMousePressedEventHandler());
label2.addEventFilter( MouseEvent.MOUSE_DRAGGED, nodeGestures.getOnMouseDraggedEventHandler());
Label label3 = new Label("Draggable node 3");
label3.setTranslateX(200);
label3.setTranslateY(200);
label3.addEventFilter( MouseEvent.MOUSE_PRESSED, nodeGestures.getOnMousePressedEventHandler());
label3.addEventFilter( MouseEvent.MOUSE_DRAGGED, nodeGestures.getOnMouseDraggedEventHandler());
Circle circle1 = new Circle( 300, 300, 50);
circle1.setStroke(Color.ORANGE);
circle1.setFill(Color.ORANGE.deriveColor(1, 1, 1, 0.5));
circle1.addEventFilter( MouseEvent.MOUSE_PRESSED, nodeGestures.getOnMousePressedEventHandler());
circle1.addEventFilter( MouseEvent.MOUSE_DRAGGED, nodeGestures.getOnMouseDraggedEventHandler());
Rectangle rect1 = new Rectangle(100,100);
rect1.setTranslateX(450);
rect1.setTranslateY(450);
rect1.setStroke(Color.BLUE);
rect1.setFill(Color.BLUE.deriveColor(1, 1, 1, 0.5));
rect1.addEventFilter( MouseEvent.MOUSE_PRESSED, nodeGestures.getOnMousePressedEventHandler());
rect1.addEventFilter( MouseEvent.MOUSE_DRAGGED, nodeGestures.getOnMouseDraggedEventHandler());
canvas.getChildren().addAll(label1, label2, label3, circle1, rect1);
group.getChildren().add(canvas);
// create scene which can be dragged and zoomed
Scene scene = new Scene(group, 1024, 768);
SceneGestures sceneGestures = new SceneGestures(canvas);
scene.addEventFilter( MouseEvent.MOUSE_PRESSED, sceneGestures.getOnMousePressedEventHandler());
scene.addEventFilter( MouseEvent.MOUSE_DRAGGED, sceneGestures.getOnMouseDraggedEventHandler());
scene.addEventFilter( ScrollEvent.ANY, sceneGestures.getOnScrollEventHandler());
stage.setScene(scene);
stage.show();
canvas.addGrid();
}
}

As nobody answered the question up until now and I stumbled over the same problem, I will post my solution, which adds a simple calculation of the left/up/lower and right overhang of nodes.
If you replace the part of the zooming-code with the part attached below, you should be good to got.
//maxX = right overhang, maxY = lower overhang
double maxX = canvas.getBoundsInParent().getMaxX() - canvas.localToParent(canvas.getPrefWidth(), canvas.getPrefHeight()).getX();
double maxY = canvas.getBoundsInParent().getMaxY() - canvas.localToParent(canvas.getPrefWidth(), canvas.getPrefHeight()).getY();
// minX = left overhang, minY = upper overhang
double minX = canvas.localToParent(0,0).getX() - canvas.getBoundsInParent().getMinX();
double minY = canvas.localToParent(0,0).getY() - canvas.getBoundsInParent().getMinY();
// adding the overhangs together, as we only consider the width of canvas itself
double subX = maxX + minX;
double subY = maxY + minY;
// subtracting the overall overhang from the width and only the left and upper overhang from the upper left point
double dx = (event.getSceneX() - ((canvas.getBoundsInParent().getWidth()-subX)/2 + (canvas.getBoundsInParent().getMinX()+minX)));
double dy = (event.getSceneY() - ((canvas.getBoundsInParent().getHeight()-subY)/2 + (canvas.getBoundsInParent().getMinY()+minY)));
WARNING: The left and up overhang will always be computed correctly, but I did not find any working way, to compute the right and lower overhang of nodes without the use of the preferred height and width attributes. So keep in mind, that you need these.
Also, you can improve the performance by only computing the canvas.getBoundsInParent() thing only once before as well as the the other calculations that are computed multiple times.
Hope it helps someone.

Related

Clearing past drawings on javafx using timeline

I am trying to make a snake game using maven project on JavaFX. In my code snake was moving but the previous coordinates are not cleared on the scene. Are there any solution for this? Here is my code:
public class Main extends Application {
int sceneX = 600;
int sceneY = 400;
ArrayList <Point> snake = new ArrayList<>();
private GraphicsContext gc;
private void run(GraphicsContext gc) {
drawSnake(gc);
for (int i = snake.size()-1; i>=1; i--) {
snake.get(i).x = snake.get(i - 1).x;
snake.get(i).y = snake.get(i - 1).y;
}
snake.get(0).x++;
}
private void drawSnake(GraphicsContext gc) {
for (int i =0; i<3; i++) {
gc.fillRoundRect(snake.get(i).getX(), snake.get(i).getY(),5,5,3,3);
}
}
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("SNAKE");
Group root = new Group();
Canvas canvas = new Canvas(sceneX, sceneY);
root.getChildren().add(canvas);
Scene scene = new Scene (root);
primaryStage.setScene(scene);
primaryStage.show();
gc = canvas.getGraphicsContext2D();
for (int i = 0; i<3; i++) {
snake.add(new Point(300, 300));
}
Timeline timeline = new Timeline(new KeyFrame(Duration.millis(130), e -> run(gc)));
timeline.setCycleCount(Animation.INDEFINITE);
timeline.play();
}
I agree with what #jewelsea mentioned in the comments.
If you don't have very complex drawings/painting on the canvas, I think using scenegraph approach should make things simple.
If you opt for canvas, one possible drawback I can think of is : you need to redraw the entire canvas. Because clearing only part of the rectangle will not be a solution if you have some layered drawings (like background.. etc)
private void run(GraphicsContext gc) {
for (int i = 0; i < snake.size(); i++) {
gc.clearRect(snake.get(i).x, snake.get(i).y, size, size);
}
int lastIndex = snake.size() - 1;
for (int i = 0; i < lastIndex; i++) {
snake.get(i).x = snake.get(i + 1).x;
}
snake.get(lastIndex).x += size;
drawSnake(gc);
}
In the below gif for canvas approach, clearing the snake alone is not sufficient if I have some background.
On the other hand, if I use scenegraph approach, once we include the building blocks of the snake, all we need to do is to update the position of the building blocks.
private void run() {
int lastIndex = shapes.size() - 1;
for (int i = 0; i < lastIndex; i++) {
shapes.get(i).setLayoutX(shapes.get(i + 1).getLayoutX());
}
shapes.get(lastIndex).setLayoutX(shapes.get(lastIndex).getLayoutX() + size);
}
Below is the complete demo differentiating the two approaches:
import javafx.animation.Animation;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.control.Label;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;
import java.awt.Point;
import java.util.ArrayList;
import java.util.List;
public class SnakeGameDemo extends Application {
int width = 300;
int height = 200;
int startX = 100;
int startY = 100;
int size = 5;
int speed = 250;
List<Point> snake = new ArrayList<>();
List<Rectangle> shapes = new ArrayList<>();
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("SNAKE");
GridPane root = new GridPane();
root.setPadding(new Insets(10));
root.setHgap(10);
root.setVgap(10);
root.addRow(0, new Label("Canvas"), new Label("SceneGraph"));
root.addRow(1, canvasApproach(), scenegraphApproach());
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
private Node canvasApproach() {
Canvas canvas = new Canvas(width, height);
GraphicsContext gc = canvas.getGraphicsContext2D();
gc.setFill(Color.RED);
gc.fillRoundRect(0, 0, width, height, 0, 0);
gc.setFill(Color.GREEN);
for (int i = 0; i < 3; i++) {
snake.add(new Point(startX + (i * size), startY));
}
Timeline timeline = new Timeline(new KeyFrame(Duration.millis(speed), e -> run(gc)));
timeline.setCycleCount(Animation.INDEFINITE);
timeline.play();
return canvas;
}
private void run(GraphicsContext gc) {
for (int i = 0; i < snake.size(); i++) {
gc.clearRect(snake.get(i).x, snake.get(i).y, size, size);
}
int lastIndex = snake.size() - 1;
for (int i = 0; i < lastIndex; i++) {
snake.get(i).x = snake.get(i + 1).x;
}
snake.get(lastIndex).x += size;
drawSnake(gc);
}
private void drawSnake(GraphicsContext gc) {
snake.forEach(block -> {
gc.fillRoundRect(block.getX(), block.getY(), size, size, 3, 3);
});
}
private Node scenegraphApproach() {
Pane pane = new Pane();
pane.setStyle("-fx-background-color:red;");
pane.setPrefSize(width, height);
for (int i = 0; i < 3; i++) {
Rectangle block = new Rectangle(size, size, Color.GREEN);
// Position the shapes in the Pane using the layoutX/Y properties.
block.setLayoutX(startX + (i * size));
block.setLayoutY(startY);
pane.getChildren().add(block);
shapes.add(block);
}
Timeline timeline = new Timeline(new KeyFrame(Duration.millis(speed), e -> run()));
timeline.setCycleCount(Animation.INDEFINITE);
timeline.play();
return pane;
}
private void run() {
int lastIndex = shapes.size() - 1;
for (int i = 0; i < lastIndex; i++) {
shapes.get(i).setLayoutX(shapes.get(i + 1).getLayoutX());
}
shapes.get(lastIndex).setLayoutX(shapes.get(lastIndex).getLayoutX() + size);
}
}

Drawing with transparency on JavaFX Canvas using PixelWriter

Does anyone know why transparency drawing on a Canvas works perfectly fine using drawImage(), but doesn't work at all with a PixelWriter? I initially thought it may have something to do with Blend or some other mode/setting on the canvas/context, but haven't had any luck with that yet.
I need per-pixel variable transparency, not a single transparency value for the entire draw operation. I'll be rendering a number of "layers" (similar to how GIMP layers work, with optional transparency per-pixel). An additional open question is whether I'm better off first drawing the FINAL intended output to a WritableImage and then just drawing to the Canvas, for performance reasons, but that seems to defeat the point of using a Canvas in the first place...
Below is an example which shows a partially transparent Color being first drawn to an Image and then to the Canvas, and directly to the Canvas with setColor(). The transparent area is the Image draw, the opaque area is the setColor part. How do we get setColor() to respect Color alpha transparency for each pixel?
import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.image.WritableImage;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.paint.Color;
import javafx.scene.paint.Paint;
import javafx.stage.Stage;
public class TransparencyTest extends Application {
private static final int width = 800;
private static final int height = 600;
private Scene scene;
private final Canvas canvas = new Canvas(width, height);
#Override
public void start(Stage stage) {
scene = new Scene(new Group(canvas));
stage.setScene(scene);
render();
stage.show();
exitOnEsc();
}
private void render() {
drawTransparentBg(canvas, 0, 0, width, height);
Color color = Color.web("#77000077");
WritableImage image = new WritableImage(200, 200);
for (int x = 0; x < 200; x++) {
for (int y = 0; y < 200; y++) {
image.getPixelWriter().setColor(x, y, color);
}
}
canvas.getGraphicsContext2D().drawImage(image, 50, 50);
for (int x = 0; x < 50; x++) {
for (int y = 0; y < 50; y++) {
canvas.getGraphicsContext2D().getPixelWriter().setColor(x, y, color);
}
}
}
public void drawTransparentBg(Canvas canvas, int xPos, int yPos, int width, int height) {
int gridSize = 8;
boolean darkX = true;
String darkCol = "#111111";
String lightCol = "#222266";
for (int x = xPos; x < canvas.getWidth(); x += gridSize) {
boolean dark = darkX;
darkX = !darkX;
if (x > width) {
break;
}
for (int y = yPos; y < canvas.getHeight(); y += gridSize) {
if (y > height) {
break;
}
dark = !dark;
String color;
if (dark) {
color = darkCol;
} else {
color = lightCol;
}
canvas.getGraphicsContext2D().setFill(Paint.valueOf(color));
canvas.getGraphicsContext2D().fillRect(x, y, gridSize, gridSize);
}
}
}
private void exitOnEsc() {
scene.addEventFilter(KeyEvent.KEY_PRESSED, event -> {
if (event.getCode().equals(KeyCode.ESCAPE)) {
Platform.exit();
}
});
}
}
The GraphicsContext begins with the default BlendMode, and all forms of drawImage() use the current mode. In contrast, PixelWriter methods replace values, ignoring the BlendMode.
The example below lets you experiment with the supported BlendMode values to see the effect. Related examples are shown here and here.
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.control.ChoiceBox;
import javafx.scene.effect.BlendMode;
import javafx.scene.image.WritableImage;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
public class TransparencyTest extends Application {
private static final int S = 8;
private static final int W = S * 36;
private static final int H = W;
private final Canvas canvas = new Canvas(W, H);
private final GraphicsContext g = canvas.getGraphicsContext2D();
private BlendMode mode = g.getGlobalBlendMode();
#Override
public void start(Stage stage) {
render(mode);
BorderPane root = new BorderPane(new Pane(canvas));
ObservableList<BlendMode> modes
= FXCollections.observableArrayList(BlendMode.values());
ChoiceBox<BlendMode> cb = new ChoiceBox<>(modes);
cb.setValue(mode);
cb.valueProperty().addListener((o) -> {
render(cb.getValue());
});
root.setBottom(cb);
stage.setScene(new Scene(root));
stage.show();
}
private void render(BlendMode mode) {
drawBackground();
g.setGlobalBlendMode(mode);
Color color = Color.web("#7f00007f");
int s = 24 * 8;
WritableImage image = new WritableImage(s, s);
for (int x = 0; x < s; x++) {
for (int y = 0; y < s; y++) {
image.getPixelWriter().setColor(x, y, color);
}
}
s = 6 * 8;
g.drawImage(image, s, s);
for (int x = 0; x < s; x++) {
for (int y = 0; y < s; y++) {
g.getPixelWriter().setColor(x, y, color);
}
}
}
public void drawBackground() {
g.setGlobalBlendMode(BlendMode.SRC_OVER);
Color darkCol = Color.web("#333333");
Color lightCol = Color.web("#cccccc");
boolean dark = false;
for (int x = 0; x < W; x += S) {
dark = !dark;
for (int y = 0; y < H; y += S) {
dark = !dark;
g.setFill(dark ? darkCol : lightCol);
g.fillRect(x, y, S, S);
}
}
}
public static void main(String[] args) {
launch(args);
}
}

QPropertyAnimation for QGraphics does not work

So I have looked at other similar questions on stackoverflow but none seem to help. The problem is that the animation doesn't occur, but once I click somewhere in the QGraphicsView, it updates to the end position.
I'm animating QGraphicsRectItem with QPropertyAnimation, so I made a new class and extended QObject and QGraphicsRectItem:
class MyGraphicsRectItem : public QObject, public QGraphicsRectItem {
Q_OBJECT
Q_PROPERTY(QPointF position READ position WRITE setPosition)
Q_PROPERTY(QRectF geometry READ geometry WRITE setGeometry)
public:
...
QPointF position();
void setPosition(QPointF animBox);
QRectF geometry();
void setGeometry(QRectF geo);
...
}
One common problem with regarding this problem is that QPropertyAnimation goes out of scope when the function finishes, but I think I circumvented this problem using QAbstractAnimation::DeleteWhenStopped. In my MainWindow.cpp, I have:
group = new QParallelAnimationGroup;
for (int i = 0; i < 10 ; i += 1) {
MyGraphicsRectItem *temp = dynamic_cast<MyGraphicsRectItem*>(histZToItem[i]);
QPropertyAnimation *anim = new QPropertyAnimation(temp, "position");
anim->setDuration(500);
anim->setStartValue(temp->pos());
QPropertyAnimation *geo = new QPropertyAnimation(temp, "geometry");
geo->setDuration(500);
geo->setStartValue(temp->rect());
geo->setEndValue(QRectF(0, 0, colWidth, -80));
if (i > anchorID) {
anim->setEndValue(QPointF(40 + spaceWidth * (i) + colWidth * (i - 1), graphScene->height() - 40));
} else {
anim->setEndValue(QPointF(40 + spaceWidth * (i + 1) + colWidth * (i), graphScene->height() - 40));
}
group->addAnimation(geo);
group->addAnimation(anim);
}
group->start(QAbstractAnimation::DeleteWhenStopped);
Any ideas?
Edit
Here are my implementations for position, setPosition, geometry and setGeometry:
QPointF MyGraphicsRectItem::position()
{
return QGraphicsRectItem::pos();
}
void MyGraphicsRectItem::setPosition(QPointF animPos)
{
QGraphicsRectItem::setPos(animPos);
}
QRectF MyGraphicsRectItem::geometry()
{
return rect();
}
void MyGraphicsRectItem::setGeometry(QRectF geo)
{
setRect(geo);
}
EDIT 2
Here's the constructor:
MyGraphicsRectItem::MyGraphicsRectItem(qreal x, qreal y, qreal width, qreal height, QGraphicsItem *parent) :QGraphicsRectItem(x, y, width, height, parent) {
setAcceptHoverEvents(true);
}

DrawCircle (GraphicsContext gc) with Canvas in Javafx

I have to do some projet to draw regular polygon with Canvas in JavaFX, and I have doubt how to design a circle with canvas using GraphicsContext
I have this point class containing the two axes (x, y)
public class Point2D {
private float mX;
private float mY;
public Point2D () {
this (0,0);
}
public Point2D (float x, float y) {
mX = x;
mY = y;
}
public float getX() {
return mX;
}
public float getY() {
return mY;
}
}
And I have this circle Class and I have doubt to made the method public void drawCircle(GraphicsContext gc)
public class Circle{
private Point2D mCenter;
private Color color;
private float mRadius;
public Circle (Point2D center, Color color, float radius ) {
this.mCenter = center;
this.color = color;
this.mRadius = radius;
}
public void drawCircle(GraphicsContext gc) { // My Doubt is here
Canvas canvas = new Canvas();
gc = canvas .getGraphicsContext2D();
gc.setFill(Color.WHITE);
gc.setStroke(Color.BLACK);
}
}
In Main JavaFX
public class PaintGeometricoFX extends Application {
private BorderPane root;
#Override
public void start(Stage primaryStage) {
Point2D p = new Point2D(0, 0);
Float radius = 4.0f;
Circle circle = new Circle(p.getX(), p.getY(),Color.BLACK,radius)
Canvas canvas = new Canvas();
GraphicsContext gc = imagem.getGraphicsContext2D();
circle.drawCircle(gc);
root.setCenter(canvas);
Scene scene = new Scene(root, 1152, 800);
primaryStage.setTitle("PAINT");
primaryStage.setResizable(false);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Stroking:
getGraphicsContext2D().strokeOval(center.x-radius, center.y-radius, radius * 2, radius * 2);
Filling:
getGraphicsContext2D().fillOval(center.x-radius, center.y-radius, radius * 2, radius * 2);
Note the 3rd and 4th parameters are diameters not radii. I had a discrepancy between ScalaFx and the correct ScalaJs output. But I checked the JavaFx documentation and it works the same:
fillOval
public void fillOval(double x,
double y,
double w,
double h)
Fills an oval using the current fill paint.
This method will be affected by any of the global common or fill attributes as specified in the Rendering Attributes Table.
Parameters:
x - the X coordinate of the upper left bound of the oval.
y - the Y coordinate of the upper left bound of the oval.
w - the width at the center of the oval.
h - the height at the center of the oval.
Stroking:
getGraphicsContext2D().strokeOval(center.x-radius, center.y-radius, radius, radius);
Filling:
getGraphicsContext2D().fillOval(center.x-radius, center.y-radius, radius, radius);

My JOGL program refuses to run what is wrong?

here is my code
public class SimpleJoglApp extends JFrame {
public static void main(String[] args) {
final SimpleJoglApp app = new SimpleJoglApp();
// show what we've done
SwingUtilities.invokeLater(new Runnable() {
public void run() {
app.setVisible(true);
}
});
}
public SimpleJoglApp() {
// set the JFrame title
super("Test App");
// kill the process when the JFrame is closed
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// only two JOGL lines of code ... and here they are
GLCanvas glcanvas = new GLCanvas();
glcanvas.addGLEventListener(new SecondGLEventListener());
// add the GLCanvas just like we would any Component
getContentPane().add("Center", glcanvas);
setSize(500, 300);
}
public void centerWindow(Component frame) {
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
Dimension frameSize = frame.getSize();
if (frameSize.width > screenSize.width) {
frameSize.width = screenSize.width;
}
if (frameSize.height > screenSize.height) {
frameSize.height = screenSize.height;
}
frame.setLocation((screenSize.width - frameSize.width) >> 1, (screenSize.height - frameSize.height) >> 1);
}
public class SecondGLEventListener implements GLEventListener {
/**
* Interface to the GLU library.
*/
private GLU glu;
public void init(GLAutoDrawable drawable) {
GL gl = drawable.getGL();
glu = new GLU();
gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
gl.glViewport(0, 0, 500, 300);
gl.glMatrixMode(GL.GL_PROJECTION);
gl.glLoadIdentity();
glu.gluOrtho2D(0.0, 500.0, 0.0, 300.0);
}
/**
* Take care of drawing here.
*/
public void display(GLAutoDrawable drawable) {
float red = 0.0f;
float green = 0.0f;
float blue = 0.0f;
GL gl = drawable.getGL();
gl.glClear(GL.GL_COLOR_BUFFER_BIT);
gl.glPointSize(20.0f);
for (int i = 0; i < 10; i++) {
red -= .09f;
green -= .12f;
blue -= .15f;
if (red < 0.50) {
red = 0.5f;
}
if (green < 0.15) {
green = 0.5f;
}
if (blue < 0.15) {
blue = 0.5f;
}
gl.glPushMatrix();
gl.glColor3f(red, green, blue);
gl.glTranslatef(10.0f, 500.0f, 0.0f); // Move left 1.5 units, up 1.5 units, and back 8 units
gl.glBegin(GL.GL_QUADS); // Begin drawing quads
gl.glVertex3f((10f * i) + 10, 20.0f, 0.0f); // Top left vertex
gl.glVertex3f((40f * i) + 10, 20f, 0.0f); // Top right vertex
gl.glVertex3f((40f * i) + 10, -20f, 0.0f); // Bottom right vertex
gl.glVertex3f((10f * i) + 10, -20.0f, 0.0f); // Bottom left vertex
gl.glEnd();
gl.glPopMatrix();
}
}
public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) {}
public void displayChanged(GLAutoDrawable drawable, boolean modeChanged, boolean deviceChanged) {}
}
}
`
//~ Formatted by Jindent --- http://www.jindent.com
the code always produces the following error
Error: Could not find or load main class org.yourorghere.SimpleJoglApp
Java Result: 1
What am i doing wrong?
You never set the jframe.setvisible(true);
Also: Error: Could not find or load main class org.yourorghere.SimpleJoglApp Java Result: 1
has nothing to do with java. Just fix your classes set up.

Resources