How do i connect two nodes with a line - java

How do I connect a node which lies in the first container to another node in the second container, i.e if i have a pane which has a sub node in it how will i connect it to another pane sub node, I've only manged to connect nodes that are is the same container but that's is not what i need, i want something like this.
enter code here
public class Main extends Application {
static Pane root = new Pane();
#Override
public void start(Stage primaryStage) throws Exception{
Circuit c1 = new Circuit(10,10);
Circuit c2 = new Circuit(200,10);
Circuit c3 = new Circuit(10,200);
Circuit c4 = new Circuit(200,200);
root.getChildren().addAll(c1, c2, c3, c4);
primaryStage.setScene(new Scene(root, 300, 275));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
The circuit Class
enter code here
import static sample.Main.root;
public class Circuit extends Pane{
Circuit(int LOCATION_X, int LOCATION_Y){
setStyle("-fx-background-color: red");
setPrefSize(150,150);
setLayoutX(LOCATION_X);
setLayoutY(LOCATION_Y);
createCircle cir = new createCircle();
cir.setLayoutX(75);
cir.setLayoutY(75);
// register handlers
cir.setOnDragDetected(startHandler);
cir.setOnMouseDragReleased(dragReleaseHandler);
cir.setOnMouseDragEntered(dragEnteredHandler);
// add info allowing to identify this node as drag source/target
cir.setUserData(Boolean.TRUE);
getChildren().add(cir);
root.setOnMouseReleased(evt -> {
// mouse released outside of a target -> remove line
root.getChildren().remove(startHandler.line);
startHandler.line = null;
});
root.setOnMouseDragged(evt -> {
if (startHandler.line != null) {
Node pickResult = evt.getPickResult().getIntersectedNode();
if (pickResult == null || pickResult.getUserData() != Boolean.TRUE) {
// mouse outside of target -> set line end to mouse position
startHandler.line.setEndX(evt.getX());
startHandler.line.setEndY(evt.getY());
}
}
});
}
class DragStartHandler implements EventHandler<MouseEvent> {
public Line line;
#Override
public void handle(MouseEvent event) {
if (line == null) {
Node sourceNode = (Node) event.getSource();
line = new Line();
Bounds bounds = sourceNode.getBoundsInParent();
// start line at center of node
line.setStartX((bounds.getMinX() + bounds.getMaxX()) / 2);
line.setStartY((bounds.getMinY() + bounds.getMaxY()) / 2);
line.setEndX(line.getStartX());
line.setEndY(line.getStartY());
sourceNode.startFullDrag();
root.getChildren().add(0, line);
}
}
}
DragStartHandler startHandler = new DragStartHandler();
EventHandler<MouseDragEvent> dragReleaseHandler = evt -> {
if (evt.getGestureSource() == evt.getSource()) {
// remove line, if it starts and ends in the same node
root.getChildren().remove(startHandler.line);
}
evt.consume();
startHandler.line = null;
};
EventHandler<MouseEvent> dragEnteredHandler = evt -> {
if (startHandler.line != null) {
// snap line end to node center
Node node = (Node) evt.getSource();
Bounds bounds = node.getBoundsInParent();
startHandler.line.setEndX((bounds.getMinX()+bounds.getMaxX())/2);
startHandler.line.setEndY((bounds.getMinY()+bounds.getMaxY())/2);
}
};
}
The point from where the wire will drawn out and connected to
enter code here
public class createCircle extends Circle {
createCircle(){
super(25, Color.BLACK.deriveColor(0, 1, 1, 0.5));
}
}

You are mixing coordinate systems.
Bounds bounds = sourceNode.getBoundsInParent();
will give you the bounds of sourceNode in the coordinate system of the sourceNode's parent, which is going to be the current Circuit instance (if I read your code correctly). However, you are using those bounds to calculate the coordinates of the line, which is placed in the root node, so you need the coordinates in the coordinate system of the root.
You can transform the coordinates by doing something like
Bounds boundsInScene = sourceNode.localToScene(sourceNode.getBoundsInLocal());
Bounds boundsInRoot = root.sceneToLocal(boundsInScene);
Now boundsInRoot represents the bounds of sourceNode in the coordinate system of root, so you can use it to compute the coordinates of the line. There are likely similar transforms you need to make throughout the code.

Related

JavaFX Image drops on Y axis each time when added to the layout

I'm trying to add a bullet shooting function for a game. The bullet shoots, but every time the bullet is added to the scene it decreases on the Y axis.
My goal is to have the bullet be added to the layout at the same positions on the X and Y axis every time it is added.
I tried to do a check by using if (!(layout.getChildren().contains(bullet))) { but it still didn't work.
public class AnimationTest {
static Stage primaryStage = new Stage();
static VBox layout = new VBox(10);
static Image image = new Image("/run-gun.png");
static Image bulletI = new Image("/bullet.png");
static ImageView bullet;
public static void main(String[] args){
}
public static void start() throws Exception {
imgView = new ImageView(image);
bullet = new ImageView(bulletI);
Scene scene = new Scene(layout);
layout.getChildren().add(imgView);
primaryStage.setScene(scene);
primaryStage.show();
scene.setOnKeyPressed(e -> {
getKey(e);
});
}
public static void getKey(KeyEvent e){
bullet = new ImageView(bulletI);
if (e.getCode() == KeyCode.SPACE) {
if (layout.getChildren().contains(bullet)) {
} else {
layout.getChildren().add(bullet);
bullet.setTranslateX(imgView.getTranslateX() + 110);
bullet.setTranslateY(imgView.getTranslateY() - 57.5);
posY = (int) bullet.getTranslateY();
bullet.setTranslateY(posY);
// move bullet
TranslateTransition tt = new TranslateTransition(Duration.millis(750), bullet);
tt.setByX(1920);
tt.setCycleCount(1);
tt.setAutoReverse(false);
tt.setOnFinished(new EventHandler<ActionEvent>() {
public void handle(ActionEvent event) {
try {
removeBullet();
} catch (Exception e) {
e.printStackTrace();
}
}
});
tt.play();
}
} else {
}
}
}
I want the bullet image to ALWAYS be added to the layout in the same X and Y positions. Thanks!
----- EDIT -----
```java
if (e.getCode() == KeyCode.SPACE) {
if (layout.getChildren().contains(bullet)) {
} else {
layout.getChildren().add(bullet);
bullet.setTranslateX(imgView.getTranslateX() + 110);
bullet.setTranslateY(imgView.getTranslateY() - 57.5);
// move bullet
TranslateTransition tt = new TranslateTransition(Duration.millis(750), bullet);
tt.setByX(1920);
tt.setCycleCount(1);
tt.setAutoReverse(false);
tt.setOnFinished(new EventHandler<ActionEvent>() {
public void handle(ActionEvent event) {
try {
removeBullet();
bullet.setTranslateX(0);
bullet.setTranslateY(0);
} catch (Exception e) {
e.printStackTrace();
}
}
});
tt.play();
}
} else {
}
It still doesn't work, and I cant tell why.
For this kind of absolute positioning it is not a good idea to use any kind of layout pane (like your HBox) because that will always interfere with your positioning. It is better to just use a Pane here which does not do any layout.
What do you think is happening here?
bullet.setTranslateX(imgView.getTranslateX() + 110);
bullet.setTranslateY(imgView.getTranslateY() - 57.5);
As these values are never reset (set to 0.0 for example), they will just add up everytime.
sorry for the late post but the answer was to use a Pane instead of a VBox. With a bit of extra position editing to re-adjust after trying to make up for using a VBox I finally made the gun shoot each bullet on the same Y position. Thanks to everyone that helped! :)

Placeholder for TreeView?

I have a TreeView which is empty at the start and I want to set a placeholder until it is empty. Like the one available for ListView (setPlaceholder())
My first thought was to wrap the TreeView into a BorderPane and just change the center based on the number of elements in the TreeView. The problem is though that I add elements to the TreeView through drag and drop and if I set a label to the center for substituting a placeholder, I won't be able to drag n drop my items in the TreeView anymore. Any suggestions would be much appreciated.
TreeView has no placeholder support - not entirely certain, why not, but could be that an empty tree (whatever that means: null root? root without children? root not showing?) is a rare species.
It's rather simple to implement, though, by following the implementation for a virtualized control that supports it, f.i. TableView. All we need is a custom TreeViewSkin that
manages (creates and adds to the tree's hierarchy, layouts as needed) the placeholder
listens to relevant state of the tree and updates the placeholder's visibilty as appropriate
An example, toggling the emptyness by toggling the tree's root between null/not null via a button:
public class TreeViewWithPlaceholder extends Application {
private static class TreeViewPlaceholderSkin<T> extends TreeViewSkin<T> {
private StackPane placeholderRegion;
private Label placeholderLabel;
public TreeViewPlaceholderSkin(TreeView<T> control) {
super(control);
installPlaceholderSupport();
}
private void installPlaceholderSupport() {
registerChangeListener(getSkinnable().rootProperty(), e -> updatePlaceholderSupport());
updatePlaceholderSupport();
}
/**
* Updating placeholder/flow visibilty depending on whether or not the tree
* is considered empty.
*
* Basically copied from TableViewSkinBase.
*/
private void updatePlaceholderSupport() {
if (isTreeEmpty()) {
if (placeholderRegion == null) {
placeholderRegion = new StackPane();
placeholderRegion.getStyleClass().setAll("placeholder");
getChildren().add(placeholderRegion);
placeholderLabel = new Label("No treeItems");
placeholderRegion.getChildren().setAll(placeholderLabel);
}
}
getVirtualFlow().setVisible(!isTreeEmpty());
if (placeholderRegion != null)
placeholderRegion.setVisible(isTreeEmpty());
}
#Override
protected void layoutChildren(double x, double y, double w, double h) {
super.layoutChildren(x, y, w, h);
if (placeholderRegion != null && placeholderRegion.isVisible()) {
placeholderRegion.resizeRelocate(x, y, w, h);
}
}
private boolean isTreeEmpty() {
return getSkinnable().getRoot() == null;
}
}
private Parent createContent() {
TreeView<String> tree = new TreeView<>() {
#Override
protected Skin<?> createDefaultSkin() {
return new TreeViewPlaceholderSkin<>(this);
}
};
Button toggle = new Button("toggleRoot");
toggle.setOnAction(e -> {
TreeItem<String> root = tree.getRoot();
tree.setRoot(root == null ? new TreeItem<>("root") : null);
});
BorderPane content = new BorderPane(tree);
content.setBottom(toggle);
return content;
}
#Override
public void start(Stage stage) throws Exception {
stage.setScene(new Scene(createContent()));
//stage.setTitle(FXUtils.version());
stage.show();
}
public static void main(String[] args) {
launch(args);
}
#SuppressWarnings("unused")
private static final Logger LOG = Logger
.getLogger(TreeViewWithPlaceholder.class.getName());
}

JavaFX Draw a straight line that updates itself when the user moves the moves [duplicate]

This question already has an answer here:
how to draw a straight line in javafx that updates itself when the user moves the mouse?
(1 answer)
Closed 5 years ago.
I want to draw a straight line that updates itself, kinda like what you've in Microsoft Paint. Currently I can draw it without the visual feedback, setting the starting x and y on the first click and when user clicks the mouse button again it sets the end x and y, and adds it to root children.
I found this answer how to draw a straight line in javafx that updates itself when the user moves the mouse?, which is basically what I would like to do and essentially solves my problem by using canvas. Other user's in that thread have suggested or implicated that it would be even easier to achieve without canvas, but have not provided examples.
Since I can't comment that question: Would someone show me an example of this without canvas? According user comments, it should be even easier, but I can't figure it out.
I hacked together this, which works but I feel like there is a better way to do this:
private void registerMouseEventHandlers() {
final Toggle toggle = new Toggle();
final CustomLineMouseEventHandler lineMouseEventHandler = new CustomLineMouseEventHandler();
this.scene.addEventFilter(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {
public void handle(MouseEvent event) {
toggle.clickCount++;
if (toggle.clickedTwice()) {
lineMouseEventHandler.setEndXAndYAndAddToSceneGroup(event.getX(), event.getY());
}
else {
lineMouseEventHandler.setCustomLine(new CustomLine());
lineMouseEventHandler.setStartXAndY(event.getX(), event.getY());
}
}
});
}
public class CustomLineMouseEventHandler {
private CustomLine customLine;
private List<CustomLine> customLines = new ArrayList<CustomLine>();
public void setCustomLine(CustomLine customLine) {
this.customLine = customLine;
this.customLine.setVisible(true);
}
public void setStartXAndY(double x, double y) {
this.customLine.setStartX(x);
this.customLine.setStartY(y);
LineManipulator.getGroup().getChildren().add(this.customLine);
}
public void updateLine(double x, double y) {
if (this.customLine != null) {
this.customLine.setEndX(x);
this.customLine.setEndY(y);
}
}
public void setEndXAndYAndAddToSceneGroup(double x, double y) {
this.customLine.setEndX(x);
this.customLine.setEndY(y);
this.customLines.add(customLine);
LineManipulator.getGroup().getChildren().remove(LineManipulator.getGroup().getChildren().size() - 1);
LineManipulator.getGroup().getChildren().add(this.customLine);
this.customLine = null;
}
}
You can easily implement a click-drag-release user input style for this: add a new line to a pane when the mouse is pressed, and update its endpoint when the mouse is dragged:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.shape.Line;
import javafx.stage.Stage;
public class Draw extends Application {
private Line currentLine ;
#Override
public void start(Stage primaryStage) {
Pane pane = new Pane();
pane.setOnMousePressed(e -> {
currentLine = new Line(e.getX(), e.getY(), e.getX(), e.getY());
pane.getChildren().add(currentLine);
});
pane.setOnMouseDragged(e -> {
currentLine.setEndX(e.getX());
currentLine.setEndY(e.getY());
});
Scene scene = new Scene(pane, 600, 600);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
If you prefer a click-move-click user experience to press-drag-release, replace the two event handlers with
pane.setOnMouseClicked(e -> {
if (currentLine == null) {
currentLine = new Line(e.getX(), e.getY(), e.getX(), e.getY());
pane.getChildren().add(currentLine);
} else {
currentLine = null ;
}
});
pane.setOnMouseMoved(e -> {
if (currentLine != null) {
currentLine.setEndX(e.getX());
currentLine.setEndY(e.getY());
}
});

JavaFX: handle key combination and mouse event simultaneously

I need to react on a key + mouse event combination like:
Ctrl + Shift + R + left_mousebutton_clicked
But I can't figure out, how to handle the "left_mousebutton_clicked" only if the key combination of Ctrl + Shift + R occurs.
A solution like
if(MouseEvent.isControlDown())
will not work cause there may be different key combinations with any kind of the letters.
Any ideas?
You can use a container to store the currently pressed keys:
private final Set<KeyCode> pressedKeys = new HashSet<>();
You can attach listeners to the Scene of the control the you want to target with the mouse-click:
scene.setOnKeyPressed(e -> pressedKeys.add(e.getCode()));
scene.setOnKeyReleased(e -> pressedKeys.remove(e.getCode()));
While these listeners maintain the set, you can simply attach a listener on the target Node:
Label targetLabel = new Label("Target Label");
targetLabel.setOnMouseClicked(e -> {
if (e.getButton() == MouseButton.PRIMARY &&
pressedKeys.contains(KeyCode.R) &&
e.isShortcutDown() &&
e.isShiftDown())
System.out.println("handled!");
});
Example Application:
public class MouseClickExample extends Application {
private final Set<KeyCode> pressedKeys = new HashSet<>();
public static void main(String[] args) {
launch(args);
}
#Override public void start(Stage stage) {
VBox root = new VBox();
Scene scene = new Scene(root, 450, 250);
scene.setOnKeyPressed(e -> pressedKeys.add(e.getCode()));
scene.setOnKeyReleased(e -> pressedKeys.remove(e.getCode()));
Label targetLabel = new Label("Target Label");
targetLabel.setOnMouseClicked(e -> {
if (e.getButton() == MouseButton.PRIMARY && pressedKeys.contains(KeyCode.R) && e.isShortcutDown() && e.isShiftDown())
System.out.println("handled!");
});
root.getChildren().add(targetLabel);
stage.setScene(scene);
stage.show();
}
}
Note: The meta keys are also stored in the Set but they are not used by this example. The meta keys could be also checked in the set rather than using methods on the mouse-event.
Both ctrl and shift can be done the way you aproached it there. The left mouse key is the PrimaryButton
if(mouseEvent.isControlDown() && mouseEvent.isShiftDown && mouseEvent.isPrimaryKeyDown){
// Do your stuff here
}
And for the "non special" key (like r) I thnk you need to make a global boolean - and a seperate keyevent listener for it. So:
boolean rIsDown = false;
scene.setOnKeyPressed(e -> {
if(e.getCode() == KeyCode.R){
System.out.println("r was pressed");
//set your global boolean "rIsDown" to true
}
});
scene.setOnKeyReleased(e -> {
if(e.getCode() == KeyCode.R){
System.out.println("r was released");
//set it rIsDown back to false
}
});
Then use your it together with the other conditions...
if(mouseEvent.isControlDown() && mouseEvent.isShiftDown && rIsDown && mouseEvent.isPrimaryKeyDown){
// Do your stuff here
}

Is it possible add multiple edges using TreeLayout?

I've to display a graph like a tree level in hierarchy so I use the TreeLayout included in Jung, but I don't know if it's possible use this layout to add multiple edges between nodes. And if it's not possible, how you recommend me to do that?
Thanks!
public class Visualizacion extends JApplet {
/**
* the graph
*/
Graph<String,String> graph;
Forest<String,String> tree;
Funciones f=new Funciones();
/**
* the visual component and renderer for the graph
*/
VisualizationViewer<String,String> vv;
String root;
Layout<String,String> layout;
Layout<String,String> layout2; ;
public Visualizacion(org.graphstream.graph.Graph grafito) {
graph= new DirectedSparseMultigraph<String, String>();
createTree(grafito);
MinimumSpanningForest2<String,String> prim =
new MinimumSpanningForest2<String,String>(graph,
new DelegateForest<String,String>(), DelegateTree.<String,String>getFactory(),
new ConstantTransformer(1.0));
tree = prim.getForest();
layout = new TreeLayout<String,String>(tree,100,100);
layout2 = new StaticLayout<String,String>(graph, layout);
Transformer<String,Paint>vertexPaint= new Transformer<String,Paint>(){
public Paint transform(String i){
Node node=grafito.getNode(i);
if(node.hasAttribute("ExcedeCaudal")){
return Color.RED;}
else{
return Color.lightGray;}
}
};
vv = new VisualizationViewer<String,String>(layout2, new Dimension(800,600));
vv.addGraphMouseListener(new TestGraphMouseListener<String>());
vv.setBackground(Color.white);
vv.getRenderContext().setEdgeShapeTransformer(new EdgeShape.Line());
vv.getRenderContext().setVertexLabelTransformer(new ToStringLabeller());
vv.getRenderContext().setEdgeLabelTransformer(new ToStringLabeller());
vv.getRenderContext().setVertexShapeTransformer(new ClusterVertexShapeFunction());
vv.getRenderContext().setVertexFillPaintTransformer(vertexPaint);
// add a listener for ToolTips
vv.setVertexToolTipTransformer(new ToStringLabeller());
vv.getRenderContext().setArrowFillPaintTransformer(new ConstantTransformer(Color.lightGray));
vv.getRenderContext().setEdgeLabelTransformer(new EdgeLabelTransformer<String>());
Container content = getContentPane();
final GraphZoomScrollPane panel = new GraphZoomScrollPane(vv);
content.add(panel);
final DefaultModalGraphMouse graphMouse = new DefaultModalGraphMouse();
vv.setGraphMouse(graphMouse);
//ver cual nodo se selecciona
final PickedState<String> pickedState=vv.getPickedVertexState();
pickedState.addItemListener(new ItemListener(){
#Override
public void itemStateChanged(ItemEvent e){
Object subject=e.getItem();
if(subject instanceof String){
String vertice=(String)subject;
if(pickedState.isPicked(vertice)){
System.out.println("Vertice "+vertice+" está seleccionado");
}
else{
System.out.println("Vertice "+vertice+"no está seleccionado");
}
}
}
});
}private void createTree(org.graphstream.graph.Graph grafito) {
for(Node node:grafito){
graph.addVertex(node.getId());
}
int count=0;
for (Edge edge: grafito.getEachEdge()){
String padre=edge.getNode0().getId();
String hijo=edge.getNode1().getId();
String caudal=(edge.getAttribute("Caudal"));
graph.addEdge(caudal+"-"+count, padre,hijo,EdgeType.DIRECTED);
System.out.println("intento agregar "+edge.getAttribute("Caudal")+" cuyo padre es "+padre+" e hijo "+hijo);
count++;
}}private class EdgeLabelTransformer<V>implements Transformer<V,String>{
public String transform(V v){
return v.toString().split("-")[0];
}
}
class ClusterVertexShapeFunction<V> extends EllipseVertexShapeTransformer<V> {
ClusterVertexShapeFunction() {
setSizeTransformer(new ClusterVertexSizeFunction<V>(20));
}
#SuppressWarnings("unchecked")
#Override
public Shape transform(V v) {
if(v instanceof Graph) {
int size = ((Graph)v).getVertexCount();
if (size < 8) {
int sides = Math.max(size, 3);
return factory.getRegularPolygon(v, sides);
}
else {
return factory.getRegularStar(v, size);
}
}
return super.transform(v);
}
}
class ClusterVertexSizeFunction<V> implements Transformer<V,Integer> {
int size;
public ClusterVertexSizeFunction(Integer size) {
this.size = size;
}
public Integer transform(V v) {
if(v instanceof Graph) {
return 30;
}
return size;
}
}
static class TestGraphMouseListener<V> implements GraphMouseListener<V> {
public void graphClicked(V v, MouseEvent me) {
if(me.getClickCount()==2){
System.err.println("Vertex "+v+" fui doble click");
}
System.err.println("Vertex "+v+" was clicked at ("+me.getX()+","+me.getY()+")");
}
public void graphPressed(V v, MouseEvent me) {
System.err.println("Vertex "+v+" was pressed at ("+me.getX()+","+me.getY()+")");
}
public void graphReleased(V v, MouseEvent me) {
System.err.println("Vertex "+v+" was released at ("+me.getX()+","+me.getY()+")");
}
}
public void execute(org.graphstream.graph.Graph grafito){
JFrame frame = new JFrame();
Container content = frame.getContentPane();
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
content.add(new Visualizacion(grafito));
frame.pack();
frame.setVisible(true);}}
JUNG Layout instances determine vertex positions, not edge positions; edge rendering is determined automatically based on the edge shape that you use and the number of connecting edges.
JUNG Tree objects, however, can only have one edge connecting any pair of vertices (otherwise it's not a tree).
So if you want a Graph whose vertices are laid out using TreeLayout, but which is not directly representable as a Tree, this is what you can do:
(1) Construct a Tree which encodes the structural relationships that you want (from your original Graph), but doesn't have the parallel edges you want. You can do this either directly yourself (by constructing a copy that removing parallel edges where they exist, or just not adding them initially if that's feasible), or by using the MinimumWeightSpanningTree algorithm to extract a Tree from your original Graph.
(2) Generate a TreeLayout for this Tree.
(3) Create a StaticLayout which copies the positions used by the TreeLayout.
(4) Use this StaticLayout as a layout algorithm for your original Graph.
You can see this process demonstrated in the JUNG MinimumSpanningTreeDemo.java, in the provided sample code that's part of the JUNG source distribution. If you just want to see the code, it's here: http://jung.sourceforge.net/site/jung-samples/xref/edu/uci/ics/jung/samples/MinimumSpanningTreeDemo.html

Categories

Resources