Last active
December 8, 2020 09:14
-
-
Save jewelsea/5375786 to your computer and use it in GitHub Desktop.
Demonstrates modifying a polygon's shape in JavaFX by allowing the user to drag around control points attached to the corners of the Polygon.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import javafx.scene.Scene; | |
import javafx.application.Application; | |
import javafx.beans.property.*; | |
import javafx.beans.value.*; | |
import javafx.collections.*; | |
import javafx.event.EventHandler; | |
import javafx.scene.*; | |
import javafx.scene.input.MouseEvent; | |
import javafx.scene.paint.Color; | |
import javafx.scene.shape.*; | |
import javafx.stage.Stage; | |
/** Drag the anchors around to change a polygon's points. */ | |
public class TriangleManipulator extends Application { | |
public static void main(String[] args) throws Exception { launch(args); } | |
// main application layout logic. | |
@Override public void start(final Stage stage) throws Exception { | |
Polygon triangle = createStartingTriangle(); | |
Group root = new Group(); | |
root.getChildren().add(triangle); | |
root.getChildren().addAll(createControlAnchorsFor(triangle.getPoints())); | |
stage.setTitle("Triangle Manipulation Sample"); | |
stage.setScene( | |
new Scene( | |
root, | |
400, 400, Color.ALICEBLUE | |
) | |
); | |
stage.show(); | |
} | |
// creates a triangle. | |
private Polygon createStartingTriangle() { | |
Polygon triangle = new Polygon(); | |
triangle.getPoints().setAll( | |
100d, 100d, | |
150d, 50d, | |
250d, 150d | |
); | |
triangle.setStroke(Color.FORESTGREEN); | |
triangle.setStrokeWidth(4); | |
triangle.setStrokeLineCap(StrokeLineCap.ROUND); | |
triangle.setFill(Color.CORNSILK.deriveColor(0, 1.2, 1, 0.6)); | |
return triangle; | |
} | |
// @return a list of anchors which can be dragged around to modify points in the format [x1, y1, x2, y2...] | |
private ObservableList<Anchor> createControlAnchorsFor(final ObservableList<Double> points) { | |
ObservableList<Anchor> anchors = FXCollections.observableArrayList(); | |
for (int i = 0; i < points.size(); i+=2) { | |
final int idx = i; | |
DoubleProperty xProperty = new SimpleDoubleProperty(points.get(i)); | |
DoubleProperty yProperty = new SimpleDoubleProperty(points.get(i + 1)); | |
xProperty.addListener(new ChangeListener<Number>() { | |
@Override public void changed(ObservableValue<? extends Number> ov, Number oldX, Number x) { | |
points.set(idx, (double) x); | |
} | |
}); | |
yProperty.addListener(new ChangeListener<Number>() { | |
@Override public void changed(ObservableValue<? extends Number> ov, Number oldY, Number y) { | |
points.set(idx + 1, (double) y); | |
} | |
}); | |
anchors.add(new Anchor(Color.GOLD, xProperty, yProperty)); | |
} | |
return anchors; | |
} | |
// a draggable anchor displayed around a point. | |
class Anchor extends Circle { | |
private final DoubleProperty x, y; | |
Anchor(Color color, DoubleProperty x, DoubleProperty y) { | |
super(x.get(), y.get(), 10); | |
setFill(color.deriveColor(1, 1, 1, 0.5)); | |
setStroke(color); | |
setStrokeWidth(2); | |
setStrokeType(StrokeType.OUTSIDE); | |
this.x = x; | |
this.y = y; | |
x.bind(centerXProperty()); | |
y.bind(centerYProperty()); | |
enableDrag(); | |
} | |
// make a node movable by dragging it around with the mouse. | |
private void enableDrag() { | |
final Delta dragDelta = new Delta(); | |
setOnMousePressed(new EventHandler<MouseEvent>() { | |
@Override public void handle(MouseEvent mouseEvent) { | |
// record a delta distance for the drag and drop operation. | |
dragDelta.x = getCenterX() - mouseEvent.getX(); | |
dragDelta.y = getCenterY() - mouseEvent.getY(); | |
getScene().setCursor(Cursor.MOVE); | |
} | |
}); | |
setOnMouseReleased(new EventHandler<MouseEvent>() { | |
@Override public void handle(MouseEvent mouseEvent) { | |
getScene().setCursor(Cursor.HAND); | |
} | |
}); | |
setOnMouseDragged(new EventHandler<MouseEvent>() { | |
@Override public void handle(MouseEvent mouseEvent) { | |
double newX = mouseEvent.getX() + dragDelta.x; | |
if (newX > 0 && newX < getScene().getWidth()) { | |
setCenterX(newX); | |
} | |
double newY = mouseEvent.getY() + dragDelta.y; | |
if (newY > 0 && newY < getScene().getHeight()) { | |
setCenterY(newY); | |
} | |
} | |
}); | |
setOnMouseEntered(new EventHandler<MouseEvent>() { | |
@Override public void handle(MouseEvent mouseEvent) { | |
if (!mouseEvent.isPrimaryButtonDown()) { | |
getScene().setCursor(Cursor.HAND); | |
} | |
} | |
}); | |
setOnMouseExited(new EventHandler<MouseEvent>() { | |
@Override public void handle(MouseEvent mouseEvent) { | |
if (!mouseEvent.isPrimaryButtonDown()) { | |
getScene().setCursor(Cursor.DEFAULT); | |
} | |
} | |
}); | |
} | |
// records relative x and y co-ordinates. | |
private class Delta { double x, y; } | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@jewelsea Thanks very much for this (and all the many other great sample projects), they're very very helpful for beginners!
The only thing I couldn't find was a nice sample project of how a bigger project might be managed with some of these methods & calls split into a MVC format but I appreciate all your efforts, thanks again.