I'm new to JavaFX and I have troubles with bindings.
I'm coding a game in which I draw lines between panes contains in the cells of a GridPane. I can draw lines with the code below, but I'd like to have the line coordinates to scale as the pane gets bigger when I resize the window.
The Panes and the GridPane already resize as wanted, and I've managed to bind the line's width to the size of the scene (cellSize is a binding on the scene width/height).
Pane lastPane = panes[lastC][lastR];
Pane curPane = panes[c][r];
Bounds boundsInSceneLast = lastPane.localToScene(lastPane.getBoundsInLocal());
Bounds boundsInSceneCur = curPane.localToScene(curPane.getBoundsInLocal());
double lastX = (boundsInSceneLast.getMinX() + boundsInSceneLast.getMaxX())/2;
double lastY = (boundsInSceneLast.getMinY() + boundsInSceneLast.getMaxY())/2;
double x = (boundsInSceneCur.getMinX() + boundsInSceneCur.getMaxX())/2;
double y= (boundsInSceneCur.getMinY() + boundsInSceneCur.getMaxY())/2;
Line line = new Line(lastX, lastY, x, y);
line.setStroke(Color.web(couleurs.get(symbole)));
line.strokeWidthProperty().bind(cellSize.divide(10));
line.setStrokeLineCap(StrokeLineCap.BUTT);
anchor.getChildren().add(line);
lines.get(symbole).add(line);
anchor is an AnchorPane which is the root of my Scene and lines is a HashMap which keeps track of the lines, but those shouldn't be relevant to my problem.
I'm sure it can be done pretty simply but I've searched the web pretty deeply and all I could understand and try wasn't getting the job done so I'm asking for your help.
Thanks in advance !! :)
It's problematic to listen to the bounds of a Node in the scene since there is no property for the scene bounds and every Parent on the path to the root node of the scene could apply a transform that require you to adjust the position of the line ends. This would require you to add a listener to descendant of the Panes.
It would be simpler to make the lines a part of the GridPane itself as unmanaged nodes. This allows you to work with the bounds of the nodes in the GridPane:
final Pane lastPane = panes[lastC][lastR];
final Pane curPane = panes[c][r];
final Line line = new Line();
line.setStroke(Color.web(couleurs.get(symbole)));
line.strokeWidthProperty().bind(cellSize.divide(10));
line.setStrokeLineCap(StrokeLineCap.BUTT);
InvalidationListener listener = o -> {
Bounds boundsInSceneLast = lastPane.getBoundsInParent();
Bounds boundsInSceneCur = curPane.getBoundsInParent();
double lastX = (boundsInSceneLast.getMinX() + boundsInSceneLast.getMaxX())/2;
double lastY = (boundsInSceneLast.getMinY() + boundsInSceneLast.getMaxY())/2;
double x = (boundsInSceneCur.getMinX() + boundsInSceneCur.getMaxX())/2;
double y = (boundsInSceneCur.getMinY() + boundsInSceneCur.getMaxY())/2;
line.setStartX(lastX);
line.setStartY(lastY);
line.setEndX(x);
line.setEndY(y);
};
// listen to location & size changes of panes in the GridPane
lastPane.boundsInParentProperty().addListener(listener);
curPane.boundsInParentProperty().addListener(listener);
// initial refresh
listener.invalidated(null);
// add line to GridPane without considering it for layout
line.setManaged(false);
gridPane.getChildren().add(line);
lines.get(symbole).add(line);
Related
Is there any way to make polyline's corners rounded in javafx. I tried stroke line join but it did not work. I want to make corners like the thin line in the image below.
Line join don't help here, since they control the behavior for 2 path elements that don't have the same direction at the point where the lines join. They don't modify the path elements themselves. You need to use Path instead.
ArcTo and QuadCurveTo provide you with options for creating round corners. Both look pretty similar. The following code let's you play around with distance from the corner where the curve starts a bit:
private Path arcPath(DoubleProperty distanceProperty) {
MoveTo moveTo = new MoveTo(300, 300);
// end the line at the given distance right of the intersection of the lines
HLineTo lineTo1 = new HLineTo();
lineTo1.xProperty().bind(distanceProperty.add(50));
ArcTo arcTo = new ArcTo();
// end the curve at the given distance above the intersection of the lines
arcTo.setX(50);
arcTo.yProperty().bind(distanceProperty.negate().add(300));
// radius is equal to the distance
arcTo.radiusXProperty().bind(distanceProperty);
arcTo.radiusYProperty().bind(distanceProperty);
arcTo.setSweepFlag(true);
VLineTo lineTo2 = new VLineTo(50);
return new Path(moveTo, lineTo1, arcTo, lineTo2);
}
private Path quadPath(DoubleProperty distanceProperty) {
MoveTo moveTo = new MoveTo(300, 300);
// end the line at the given distance right of the intersection of the lines
HLineTo lineTo1 = new HLineTo();
lineTo1.xProperty().bind(distanceProperty.add(50));
QuadCurveTo curveTo = new QuadCurveTo();
// control point at the location where the lines would intersect
curveTo.setControlX(50);
curveTo.setControlY(300);
// end the curve at the given distance above the intersection of the lines
curveTo.setX(50);
curveTo.yProperty().bind(distanceProperty.negate().add(300));
VLineTo lineTo2 = new VLineTo(50);
return new Path(moveTo, lineTo1, curveTo, lineTo2);
}
Slider distanceSlider = new Slider(0, 250, 10);
Label label = new Label();
label.textProperty().bind(distanceSlider.valueProperty().asString("%.2f"));
HBox controls = new HBox(distanceSlider, label);
Path path1 = quadPath(distanceSlider.valueProperty());
Path path2 = arcPath(distanceSlider.valueProperty());
VBox root = new VBox(new HBox(new Pane(path1), new Pane(path2)), controls);
I am developing a JaxaFX application that generates reports by taking data from a MySQL database. I came across an issue when working with piecharts. Whenever the fetched data is 0, the label getting displayed for it overlaps with the other labels in the piechart making it difficult to read.
Relevant bits of Code:
Fetching the Data:
Get_data_from_pos_basket_for_reporting getreportPaymentPaidUnpaidNoClass=new Get_data_from_pos_basket_for_reporting();
getreportPaymentPaidUnpaidNoClass.getreportPaymentPaidUnpaidNo(fromDate, untilDate);
double amountPaid,amountUnpaid;
int totalPaid,totalUnpaid;
amountPaid=getreportPaymentPaidUnpaidNoClass.getAmountPaid();
amountUnpaid= getreportPaymentPaidUnpaidNoClass.getAmountUnpaid();
totalPaid=getreportPaymentPaidUnpaidNoClass.getTotalPaid();
totalUnpaid=getreportPaymentPaidUnpaidNoClass.getTotalUnpaid();
double totalamountPaidUnpaid=amountPaid+paymentSystem+amountUnpaid;
double totalNoPaidUnpaid=totalPaid+totalUnpaid;
// DecimalFormat df = new DecimalFormat("0.00");
double percentagePaidAmount=((amountPaid/totalamountPaidUnpaid)*100);
double percentageUnpaidAmount=((amountUnpaid/totalamountPaidUnpaid)*100);
double percentagePaidTotal=((totalPaid/totalNoPaidUnpaid)*100);
double percentageUnpaidTotal=((totalUnpaid/totalNoPaidUnpaid)*100);
Setting the Piechart:
ObservableList<PieChart.Data> pieChartData3 =
FXCollections.observableArrayList(
new PieChart.Data("Paid\n"+(df.format(percentagePaidAmount))+"%", amountPaid),
new PieChart.Data("Unpaid\n"+(df.format(percentageUnpaidAmount))+"%", amountUnpaid));
PieChart chart3 = new PieChart(pieChartData3);
chart3.getStyleClass().add("chart-legend");
chart3.setTitle("Payment Details[Total Amount]");
VBox vBoxPie3= new VBox();
vBoxPie3.getChildren().addAll(chart3);
Please suggest a solution such that the labels don't overlap with each other.
I am using JGraphX, and I have a vertex with 3 ports, but I want the vertex to be to the front, for some reason it is not bringing the vertex to the front, what could I be missing?
final int PORT_DIAMETER = 20;
final int PORT_RADIUS = PORT_DIAMETER / 2;
mxGeometry geo1 = new mxGeometry(0, 0.5, PORT_DIAMETER,
PORT_DIAMETER);
// Because the origin is at upper left corner, need to translate to
// position the center of port correctly
geo1.setOffset(new mxPoint(-PORT_RADIUS, -PORT_RADIUS));
geo1.setRelative(true);
geo1.setWidth(10);
mxCell port1 = new mxCell(null, geo1,
"port;image=/Images/blue-right-arrow.png");
port1.setVertex(true);
mxGeometry geo2 = new mxGeometry(1.0, 0.5, PORT_DIAMETER,
PORT_DIAMETER);
geo2.setOffset(new mxPoint(-PORT_RADIUS, -PORT_RADIUS));
geo2.setRelative(true);
mxCell port2 = new mxCell(null, geo2,
"port;image=/Images/blue-right-arrow.png");
port2.setVertex(true);
mxGeometry geo3 = new mxGeometry(0.5, 1, PORT_DIAMETER,
PORT_DIAMETER);
geo3.setOffset(new mxPoint(-PORT_RADIUS, -PORT_RADIUS));
geo3.setRelative(true);
mxCell port3 = new mxCell(null, geo3,
"port;image=/Images/blue-up-arrow.png");
port3.setVertex(true);
graph.addCell(port1, this);
graph.addCell(port2, this);
graph.addCell(port3, this);
graph.cellsOrdered(new Object[]{port1,port2,port3}, true);
graph.cellsOrdered(new Object[]{this}, false);
I am not sure whether this is possible: the ports have a parent p1 (in your case some 'this') and p1 has a parent e.g. p2 (or the default parent). when you bring to front or back a cell you just alter the index of the cell on the same parent. In your case, if you change the parent of the ports, you will loose them because of the relative geometry (that is necessary).
You can verify this in the example GraphEditor class: create a large rectangle, then right click on the rectangle->shape->ender group, then create a second shape, then right click shape->exit group. Then try to bring to back the second shape: right click on to the shape shape->to back. Nothing changes.
One solution, if it suits you, is to create another cell on top of your 'this' cell (the one with the ports):
double w = this.getGeometry().getWidth();
double h = this.getGeometry().getHeight();
mxGeometry geo4 = new mxGeometry(0, 0, w, h);
geo4.setOffset(new mxPoint(-PORT_RADIUS, -PORT_RADIUS));
String s = this.getStyle(); //set the same style
mxCell c = new mxCell(null, geo4, style);
c.setVertex(true);
graph.addCell(c, this); //set this to be the patern
Another solution is to edit your blue-right-arrow.png (port) and paint the half of it with the style of your parent vertex, in order to create the illusion that they are at the back.
Hope it helps.
everyone. I am new in JavaFX, I have one problem maybe easier for you but spent me a lot of time, I still can not figure out. My problem is this:
I need to implement a swatch GUI with Java Scene Builder. I choose class Circle as swatch dial, and choose class Label to as time index(1, 2, 3, ... 12), and choose class Line as hour hand, minute hand and second hand.
When I want to do zoom in zoom out operations, I want that the Label(time Indexes) and the Line(swatch hands) can be the children of the Circle(swatch dial), but it seems can not.
So, is there any method can zoom in or zoom out together this several nodes together?
Put all the nodes in a Group and apply the zoom to the Group.
Something like
Pane mainContainer = ... ;
Group watch = new Group();
Circle dial = new Circle(...);
Line hourHand = ... ;
Line minuteHand = ... ;
watch.getChildren().addAll(dial, hourHand, minuteHand);
Label[] timeLabels = new Label[12];
for (int i=1; i<=12; i++) {
timeLabels[i-1]= new Label(String.valueOf(i));
watch.getChildren().add(timeLabels[i-1]);
}
mainContainer.getChildren().add(watch);
Obviously you need to position the various pieces.
Then to zoom just do
watch.setScaleX(...);
watch.setScaleY(...);
I've ran into a weird problem with getCharBoundaries, I could not figure out what coordinate space the coordinates returned from the function was in. What ever I tried I could not get it to match up with what I expected.
So I made a new project and and added simple code to highlight the last charater in a textfield, and all of a sudden it worked fine. I then tried to copy over the TextField that had been causing me problems, into the new project. And now the same weird offset appeared 50px on the x axis. Everything else was spot on.
So after some headscracthing comparing the two TextFields, I simply can not see a difference in their properties or transformation.
So I was hoping that someone might now what property might affect the coordinates returned by getCharBoundaries.
I am using Flash CS4.
I've just had exactly the same problem and thought I'd help out by offering what my findings are. With a help from this thread, I tried to find everything that wasn't 'default' about the textfield I was using. I found that when I had switched my TextFormatAlign (or 'align' in the IDE) and TextFieldAutoSize properties to 'LEFT' as opposed to 'CENTER', it solved the problem.
A little late in the game perhaps, but worth knowing for anyone running into the same problem. This was the only thread I could find that raised the right flag...
Well the getCharBoundaries returns the boundaries in the textfield coordinate system. Where the origin is topleft corner of the textfield.
getCharBoundaries does not take into consideration the scrolling. you need to check if there are scrollbars on its parent (textarea) and if so relocate. One quick way of doing it is using localtoglobal and globaltolocal. Use the first to translate from the textfield coordinate system to the application coordinate system and then use the second to translate from the app coordinate system to the coordinate system of the parent of the textfield which is the textarea. I'm fine tuning a my method to get char boundaries i will publish it today on my blog
http://flexbuzz.blogspot.com/
Works For Me(tm) (Flex Builder AS3 project):
[Embed(systemFont="Segoe UI", fontWeight="bold", fontName="emb",
mimeType="application/x-font")]
private var EmbeddedFont:Class;
public function ScratchAs3()
{
stage.scaleMode = 'noScale';
stage.align = 'tl';
var m:Matrix = new Matrix(.8, .1, -.1, 1.1, 26, 78);
var t:TextField = new TextField();
t.autoSize = 'left';
t.wordWrap = false;
t.embedFonts = true;
t.defaultTextFormat = new TextFormat("emb", 100, 0, true);
t.transform.matrix = m;
t.text = "TEST STRING.";
addChild(t);
var r:Rectangle = t.getCharBoundaries(8);
var tl:Point = m.transformPoint(r.topLeft);
var tr:Point = m.transformPoint(new Point(r.right, r.top));
var bl:Point = m.transformPoint(new Point(r.left, r.bottom));
var br:Point = m.transformPoint(r.bottomRight);
graphics.beginFill(0xFF, .6);
graphics.moveTo(tl.x, tl.y);
graphics.lineTo(tr.x, tr.y);
graphics.lineTo(br.x, br.y);
graphics.lineTo(bl.x, bl.y);
graphics.lineTo(tl.x, tl.y);
}
To literally answer your question, it returns the coordinates in the TextField's coordinate system, not it's parent, and it is affected by DisplayObject.transform.matrix, which is the backing for the .x, .y, .scaleX, .scaleY, .width, .height, and .rotation properties.
What ever it was the solution was simple to add a new TextField, never found out what property screwed everything up.
The first answer is correct in most cases. However if your field is parented to another movie clip it may still return the wrong y coordinate. try this code:
//if this doesn't work:
myTextFormat = new TextFormat();
myTextFormat.align = TextFormatAlign.LEFT;
myFieldsParent.myField.autoSize = TextFieldAutoSize.LEFT;
myFieldsParent.myField.setTextFormat( myTextFormat);
//try this:
var x = myFieldsParent.myField.getCharBoundaries(o).x;
var y = myFieldsParent.myField.getCharBoundaries(o).y;
var myPoint:Point = new Point(myField.getCharBoundaries(o).x,myField.getCharBoundaries(o).y);
var pt:Point = new Point(myFieldsParent.myField.getCharBoundaries(o).x, myFieldsParent.myField.getCharBoundaries(o).y);
pt = myFieldsParent.myField.localToGlobal(pt);
//pt is the variable containing the coordinates of the char in the stage's coordinate space. You may still need to offset it with a fixed value but it should be constant.
I didn't test this code as I have adapted this example from code that is embedded into my project so I apologize if I'm missing something...