Can't rotate a shape in Javafx - javafx

I'm having trouble rotating a shape in JavaFX. Here's some code in which I want the arrowhead to point in the same direction as the line (yes, I will be adding a translation later, to put it on the end of the line, but one thing at a time).
The println statement is showing me that theta is updating correctly as I move the start and end points around, but the arrowhead stays stubbornly at the original angle. Is there another step I have to do in order to get shape to rotate, or should I be doing it a different way?
package application.view;
public class Connector extends Group {
private final DoubleProperty startX;
private final DoubleProperty startY;
private final DoubleProperty endX;
private final DoubleProperty endY;
public Connector(Line line, Polygon arrowhead, Preferences prefs) {
arrowhead.setStroke(Color.BLACK);
arrowhead.setStrokeWidth(1);
arrowhead.setFill(Color.CORNFLOWERBLUE);
startX = new SimpleDoubleProperty();
startY = new SimpleDoubleProperty();
endX = new SimpleDoubleProperty();
endY = new SimpleDoubleProperty();
thetaBinding = new DoubleBinding() {
{
super.bind(startX, startY, endX, endY);
}
#Override
protected double computeValue() {
return Math.atan2(getEndY() - getStartY(), getEndX() - getStartX());
}
};
theta = new SimpleDoubleProperty();
theta.bind(thetaBinding);
theta.addListener(new ChangeListener<Number>() {
#Override
public void changed(ObservableValue<? extends Number> observable,
Number oldValue, Number newValue) {
System.out.println(newValue);
arrowhead.rotateProperty().set((Double)newValue);
}
});
lineLength = new DoubleBinding() {
{
super.bind(startX, startY, endX, endY);
}
#Override
protected double computeValue() {
return Math.hypot(getEndX() - getStartX(), getEndY() - getStartY()) - prefs.getDouble("CONNECTOR_ARROW_LENGTH", Defaults.CONNECTOR_ARROW_LENGTH);
}
};
lineEndX = new DoubleBinding() {
{
super.bind(startX, lineLength, theta);
}
#Override
protected double computeValue() {
return startX.get() + lineLength.get() * Math.cos(theta.get());
}
};
lineEndY = new DoubleBinding() {
{
super.bind(startY, lineLength, theta);
}
#Override
protected double computeValue() {
return startY.get() + lineLength.get() * Math.sin(theta.get());
}
};
arrowhead.getPoints().setAll(new Double[] {
0.0, 0.0,
0.0, prefs.getDouble("CONNECTOR_ARROW_HALF_WIDTH", Defaults.CONNECTOR_ARROW_HALF_WIDTH),
prefs.getDouble("CONNECTOR_ARROW_LENGTH", Defaults.CONNECTOR_ARROW_LENGTH), 0.0,
0.0, -prefs.getDouble("CONNECTOR_ARROW_HALF_WIDTH", Defaults.CONNECTOR_ARROW_HALF_WIDTH),
});
line.startXProperty().bind(startX);
line.startYProperty().bind(startY);
line.endXProperty().bind(lineEndX);
line.endYProperty().bind(lineEndY);
getChildren().addAll(line, arrowhead);
}
public final DoubleBinding thetaBinding;
public final DoubleProperty theta;
public final DoubleBinding lineLength;
public final DoubleBinding lineEndX;
public final DoubleBinding lineEndY;
public double getStartX() {
return startX.get();
}
public void setStartX(double value) {
startX.set(value);
}
public DoubleProperty startXProperty() {
return startX;
}
public double getStartY() {
return startY.get();
}
public void setStartY(double value) {
startY.set(value);
}
public DoubleProperty startYProperty() {
return startY;
}
public double getEndX() {
return endX.get();
}
public void setEndX(double value) {
endX.set(value);
}
public DoubleProperty endXProperty() {
return endX;
}
public double getEndY() {
return endY.get();
}
public void setEndY(double value) {
endY.set(value);
}
public DoubleProperty endYProperty() {
return endY;
}
}

Related

Exception in thread "JavaFX Application Thread" java.lang.IndexOutOfBoundsException while adding cloned object to Pane

I have the following class in which I am trying to implement the prototype pattern:
public class Element extends Group implements Cloneable{
private final double ELEMENT_WIDTH = 50;
private final double ELEMENT_HEIGHT = 70;
private final double CIRCLE_RADIUS = 1;
private int minimalNumberOfInputs;
private Shape body = new Rectangle(ELEMENT_WIDTH, ELEMENT_HEIGHT, Color.WHITE);
private Circle output = new Circle(CIRCLE_RADIUS);
private Line outputLine = new Line(ELEMENT_WIDTH, ELEMENT_HEIGHT / 2, ELEMENT_WIDTH + 15, ELEMENT_HEIGHT / 2);
private ArrayList<Circle> inputs;
private ArrayList<Line> inputLines;
private Circle inversionDesignation;
private Text symbol;
private Integer identifier;
private double bodyCorX;
private double bodyCorY;
private double corX = 0;
private double corY = 0;
private double mouseX = 0;
private double mouseY = 0;
private boolean dragging = false;
public Integer getIdentifier() {
return identifier;
}
public ArrayList<Circle> getInputs() {
return inputs;
}
public Circle getOutput() {
return output;
}
public Circle getInversionDesignation() {
return inversionDesignation;
}
public double getELEMENT_WIDTH() {
return ELEMENT_WIDTH;
}
public double getELEMENT_HEIGHT() {
return ELEMENT_HEIGHT;
}
public double getCIRCLE_RADIUS() {
return CIRCLE_RADIUS;
}
public int getMinimalNumberOfInputs() {
return minimalNumberOfInputs;
}
public Shape getBody() {
return body;
}
public Line getOutputLine() {
return outputLine;
}
public ArrayList<Line> getInputLines() {
return inputLines;
}
public Text getSymbol() {
return symbol;
}
public double getBodyCorX() {
return bodyCorX;
}
public double getBodyCorY() {
return bodyCorY;
}
public double getCorX() {
return corX;
}
public double getCorY() {
return corY;
}
public double getMouseX() {
return mouseX;
}
public double getMouseY() {
return mouseY;
}
public boolean isDragging() {
return dragging;
}
public Element(){
}
public Element(Circle inversionDesignation, Text symbol, int minimalNumberOfInputs) {
this.minimalNumberOfInputs = minimalNumberOfInputs;
this.body.setStroke(Color.BLACK);
this.body.setStrokeType(StrokeType.INSIDE);
this.body.setStrokeWidth(2.5);
this.output.setFill(Color.BLACK);
this.output.toFront();
this.inversionDesignation = inversionDesignation;
this.symbol = symbol;
this.inputs = new ArrayList<>();
this.inputLines = new ArrayList<>();
this.identifier = this.hashCode();
this.outputLine.setStrokeWidth(2);
this.createStartInputs();
this.configureInputPoints();
this.bindGraphicalElements();
elementMovementEvents();
elementEnteredEvents();
}
private void bindGraphicalElements() {
this.getChildren().add(body);
this.getChildren().add(output);
this.getChildren().add(outputLine);
this.getChildren().addAll(inputs);
this.getChildren().addAll(inputLines);
if (this.symbol != null) {
this.getChildren().add(symbol);
symbol.relocate((ELEMENT_WIDTH / 2) - symbol.getTabSize() / 2, ELEMENT_HEIGHT / 8);
symbol.setFont(new Font("Consolas", 14));
}
if (this.inversionDesignation != null) {
this.getChildren().add(inversionDesignation);
this.inversionDesignation.setStrokeType(StrokeType.INSIDE);
this.inversionDesignation.setStrokeWidth(1);
this.inversionDesignation.setStroke(Color.BLACK);
this.inversionDesignation.relocate((this.bodyCorX + this.ELEMENT_WIDTH) - (this.inversionDesignation.getRadius() + 1), (this.bodyCorY + this.ELEMENT_HEIGHT / 2) - this.inversionDesignation.getRadius());
inversionDesignation.toFront();
}
}
private void addGraphicalElement(Shape shape) {
this.getChildren().add(shape);
}
private void createStartInputs() {
for (int i = 0; i < this.minimalNumberOfInputs; i++) {
inputs.add(new Circle(CIRCLE_RADIUS));
inputs.get(i).setFill(Color.BLACK);
inputs.get(i).toFront();
}
configureInputPoints();
for (int i = 0; i < inputs.size(); i++) {
Line line = new Line(inputs.get(i).getLayoutX() - 15, inputs.get(i).getLayoutY(), inputs.get(i).getLayoutX(), inputs.get(i).getLayoutY());
line.setStrokeWidth(2);
inputLines.add(line);
}
}
private void addNewInput() {
Circle newCircle = new Circle(CIRCLE_RADIUS);
newCircle.setFill(Color.BLACK);
this.inputs.add(newCircle);
this.configureInputPoints();
this.addGraphicalElement(newCircle);
}
private void configureInputPoints() {
this.output.relocate((this.bodyCorX + this.ELEMENT_WIDTH) - (output.getRadius() + 1), (this.bodyCorY + this.ELEMENT_HEIGHT / 2) - output.getRadius());
int distance = (int) ELEMENT_HEIGHT / (inputs.size() + 1); //Растояние между точками входа.
for (int i = 0; i < inputs.size(); i++) {
inputs.get(i).relocate(this.bodyCorX - (CIRCLE_RADIUS - 1), this.bodyCorY + (distance * (i + 1) - CIRCLE_RADIUS));
}
}
private void elementMovementEvents() {
onMousePressedProperty().set(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
mouseX = event.getSceneX();
mouseY = event.getSceneY();
corX = getLayoutX();
corY = getLayoutY();
}
});
onMouseDraggedProperty().set(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
double offsetX = event.getSceneX() - mouseX; //смещение по X
double offsetY = event.getSceneY() - mouseY;
corX += offsetX;
corY += offsetY;
double scaledX = corX;
double scaledY = corY;
setLayoutX(scaledX);
setLayoutY(scaledY);
dragging = true;
mouseX = event.getSceneX();
mouseY = event.getSceneY();
event.consume();
}
});
onMouseClickedProperty().set(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
dragging = false;
}
});
}
private void testEvent() {
this.addEventHandler(MouseEvent.MOUSE_CLICKED, event -> {
this.addNewInput();
});
}
private void elementEnteredEvents() {
onMouseClickedProperty().set(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
if (event.getTarget() instanceof Circle) {
System.out.println("Circle!");
}
}
});
}
#Override
public Element clone() throws CloneNotSupportedException{
return (Element)super.clone();
}
#Override
public String toString() {
return "Element " + this.hashCode() + ": location = " + this.getLayoutX() + ": output = " + this.minimalNumberOfInputs;
}
#Override
public boolean equals(Object obj) {
if(!(obj instanceof Element)) return false;
Element element = (Element) obj;
return element.getBodyCorX() == bodyCorX && element.getBodyCorY() == bodyCorY && element.getBody() == body;
}
}
I am trying to implement a pattern using the following method:
#Override
public Element clone() throws CloneNotSupportedException{
return (Element)super.clone();
}
I have a controller like this:
public class FXMLController {
#FXML
private AnchorPane anchorPane;
#FXML
private AnchorPane workPane;
//prototypes
private Element AndPrototype = new Element(null, new Text("&"), 2);
private Element OrPrototype = new Element(null, new Text("1"), 2);
private Element NotPrototype = new Element(new Circle(5, Color.WHITE), null, 1);
private Element AndNotPrototype = new Element(new Circle(5, Color.WHITE), new Text("&"), 2);
private Element OrNotPrototype = new Element(new Circle(5, Color.WHITE), new Text("1"), 2);
#FXML
public void initialize() {
}
#FXML
private void method() throws CloneNotSupportedException {
workPane.getChildren().add(AndPrototype.clone());
}
}
In this method, I am trying to make a clone and add it to the AnchorPane
#FXML
private void method() throws CloneNotSupportedException {
workPane.getChildren().add(AndPrototype.clone());
}
As a result, when I click on the button, I get an error of the following content:
Exception in thread "JavaFX Application Thread" java.lang.IndexOutOfBoundsException: Index -1 out of bounds for length 8
at java.base/jdk.internal.util.Preconditions.outOfBounds(Preconditions.java:64)
at java.base/jdk.internal.util.Preconditions.outOfBoundsCheckIndex(Preconditions.java:70)
at java.base/jdk.internal.util.Preconditions.checkIndex(Preconditions.java:266)
at java.base/java.util.Objects.checkIndex(Objects.java:359)
at java.base/java.util.ArrayList.get(ArrayList.java:427)
at javafx.base/com.sun.javafx.collections.ObservableListWrapper.get(ObservableListWrapper.java:89)
at javafx.base/com.sun.javafx.collections.VetoableListDecorator.get(VetoableListDecorator.java:305)
at javafx.graphics/javafx.scene.Parent.updateCachedBounds(Parent.java:1704)
at javafx.graphics/javafx.scene.Parent.recomputeBounds(Parent.java:1648)
at javafx.graphics/javafx.scene.Parent.doComputeGeomBounds(Parent.java:1501)
at javafx.graphics/javafx.scene.Parent$1.doComputeGeomBounds(Parent.java:115)
at javafx.graphics/com.sun.javafx.scene.ParentHelper.computeGeomBoundsImpl(ParentHelper.java:84)
at javafx.graphics/com.sun.javafx.scene.NodeHelper.computeGeomBounds(NodeHelper.java:115)
at javafx.graphics/javafx.scene.Node.updateGeomBounds(Node.java:3847)
at javafx.graphics/javafx.scene.Node.getGeomBounds(Node.java:3809)
at javafx.graphics/javafx.scene.Node.doComputeLayoutBounds(Node.java:3657)
at javafx.graphics/javafx.scene.Node$1.doComputeLayoutBounds(Node.java:449)
at javafx.graphics/com.sun.javafx.scene.NodeHelper.computeLayoutBoundsImpl(NodeHelper.java:166)
at javafx.graphics/com.sun.javafx.scene.GroupHelper.computeLayoutBoundsImpl(GroupHelper.java:63)
at javafx.graphics/com.sun.javafx.scene.NodeHelper.computeLayoutBounds(NodeHelper.java:106)
at javafx.graphics/javafx.scene.Node$13.computeBounds(Node.java:3509)
at javafx.graphics/javafx.scene.Node$LazyBoundsProperty.get(Node.java:9782)
at javafx.graphics/javafx.scene.Node$LazyBoundsProperty.get(Node.java:9752)
at javafx.graphics/javafx.scene.Node.getLayoutBounds(Node.java:3524)
at javafx.graphics/javafx.scene.layout.AnchorPane.computeWidth(AnchorPane.java:272)
at javafx.graphics/javafx.scene.layout.AnchorPane.computeMinWidth(AnchorPane.java:248)
at javafx.graphics/javafx.scene.Parent.minWidth(Parent.java:1048)
at javafx.graphics/javafx.scene.layout.Region.minWidth(Region.java:1553)
at javafx.graphics/javafx.scene.layout.Region.computeChildPrefAreaWidth(Region.java:2012)
at javafx.graphics/javafx.scene.layout.AnchorPane.computeChildWidth(AnchorPane.java:315)
at javafx.graphics/javafx.scene.layout.AnchorPane.layoutChildren(AnchorPane.java:353)
at javafx.graphics/javafx.scene.Parent.layout(Parent.java:1207)
at javafx.graphics/javafx.scene.Scene.doLayoutPass(Scene.java:576)
at javafx.graphics/javafx.scene.Scene$ScenePulseListener.pulse(Scene.java:2476)
at javafx.graphics/com.sun.javafx.tk.Toolkit.lambda$runPulse$2(Toolkit.java:413)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:391)
at javafx.graphics/com.sun.javafx.tk.Toolkit.runPulse(Toolkit.java:412)
at javafx.graphics/com.sun.javafx.tk.Toolkit.firePulse(Toolkit.java:439)
at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:563)
at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:543)
at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.pulseFromQueue(QuantumToolkit.java:536)
at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.lambda$runToolkit$11(QuantumToolkit.java:342)
at javafx.graphics/com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:96)
at javafx.graphics/com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at javafx.graphics/com.sun.glass.ui.win.WinApplication.lambda$runLoop$3(WinApplication.java:174)
at java.base/java.lang.Thread.run(Thread.java:831)
I have absolutely no idea what this error may be connected with and in which direction they are moving.
Usually this is caused because you modified the scene graph or an attribute of a scene graph node off of the JavaFX thread.
Similar stack traces all caused by threading errors:
javafx vlcj play mutiple video get IndexOutOfBoundsException error
Exception on JavaFX when moving Labels around their container.(IndexOutOfBoundsException)
How to fix IndexOutOfBounds exception when javafx recomputes Parent/Node Bounds
How do I find out what's causing this Java FX Application Thread exception?
If it is a multi-threading issue, usually it can be fixed by either removing unnecessary threading. Or if multi-threading is unavoidable, using tools like the javafx.concurrent package or Platform.runLater to ensure nodes in the active scene graph are only modified on the JavaFX thread.
However, if you don’t have any multi-threading going on, it might be down to the weird cloning stuff you have going on which may be ill-advised. JavaFX nodes can only occur once in the scene and the clones may cause glitches in the framework.

How to create a custom ProgressIndicator in JavaFX?

Help me create a progress indicator exactly like in the image of this link:
https://imagizer.imageshack.com/img924/1038/rF9o1y.png
this progress indicator is INDETERMINATE.
I've got the solution on my problem, thanks for the idea of #Slaw.
public class AnimatedNeonCircle extends Circle {
private final RotateTransition transition;
public AnimatedNeonCircle (AnimatedNeonCircle.Animation a, double radius, double strokeWidth, Double... dashedArray) {
this.transition = new RotateTransition (a.durationProperty ().get (), AnimatedNeonCircle.this);
this.transition.setCycleCount (a.cycleCountProperty ().get ());
this.transition.setAutoReverse (a.autoReverseProperty ().get ());
this.transition.setByAngle (radius);
super.setFill (Color.TRANSPARENT);
super.setStroke (Color.CYAN);
super.setStrokeWidth (strokeWidth);
super.setEffect (new Glow (0.4));
super.setRadius (radius);
super.getStrokeDashArray ().setAll (dashedArray);
}
public void play () {
transition.play ();
}
// =======================================================================
public static class Animation {
private final SimpleObjectProperty <Duration> duration = new SimpleObjectProperty <> ();
private final SimpleIntegerProperty cycleCount = new SimpleIntegerProperty ();
private final SimpleBooleanProperty autoReverse = new SimpleBooleanProperty ();
public Animation (Duration duration, int cycleCount, boolean autoReverse) {
this.duration.set (duration);
this.cycleCount.set (cycleCount);
this.autoReverse.set (autoReverse);
}
public SimpleObjectProperty <Duration> durationProperty () { return duration; }
public SimpleIntegerProperty cycleCountProperty () { return cycleCount; }
public SimpleBooleanProperty autoReverseProperty () { return autoReverse; }
}

JavaFx - Resize Rectangle [duplicate]

This question already has an answer here:
How to change a shape property using its border in JavaFX?
(1 answer)
Closed 5 years ago.
I am try to allow users to drag/resize a rectangle in javafx.
Rectangle in javafx uses TopLeft-X, Y, height and width to draw.
What I am trying to do is to have 4 handles at each corner of the rectangle to allow users to drag the handle. Which will result in resizing the rectangle.
My application throws a stackoverflow exception which I know is caused by the recursive call of the listener.
Did i forget something? Or is there some listener that is unnecessary?
public class Handler extends Circle
{
private double deltaX;
private double deltaY;
private static final double DEFAULT_RADIUS = 10;
private static final Color DEFAULT_COLOR = Color.GOLD;
private DoubleProperty xProperty;
private DoubleProperty yProperty;
public Handler(DoubleProperty xProperty, DoubleProperty yProperty)
{
super(DEFAULT_RADIUS, DEFAULT_COLOR);
this.xProperty = xProperty;
this.yProperty = yProperty;
init();
}
private void init()
{
setFill(DEFAULT_COLOR.deriveColor(1, 1, 1, 0.5));
setStroke(DEFAULT_COLOR);
setStrokeWidth(2);
setStrokeType(StrokeType.OUTSIDE);
centerXProperty().bind(xProperty);
centerYProperty().bind(yProperty);
setOnMousePressed(new EventHandler<MouseEvent>()
{
#Override
public void handle(MouseEvent mouseEvent)
{
mouseEvent.consume();
deltaX = getCenterX() - mouseEvent.getX();
deltaY = getCenterY() - mouseEvent.getY();
getScene().setCursor(javafx.scene.Cursor.MOVE);
}
});
setOnMouseReleased(new EventHandler<MouseEvent>()
{
#Override
public void handle(MouseEvent mouseEvent)
{
getScene().setCursor(javafx.scene.Cursor.HAND);
}
});
setOnMouseEntered(new EventHandler<MouseEvent>()
{
#Override
public void handle(MouseEvent mouseEvent)
{
if (!mouseEvent.isPrimaryButtonDown())
{
getScene().setCursor(javafx.scene.Cursor.HAND);
}
}
});
setOnMouseExited(new EventHandler<MouseEvent>()
{
#Override
public void handle(MouseEvent mouseEvent)
{
if (!mouseEvent.isPrimaryButtonDown())
{
getScene().setCursor(javafx.scene.Cursor.DEFAULT);
}
}
});
setOnMouseDragged(new EventHandler<MouseEvent>()
{
#Override
public void handle(MouseEvent mouseEvent)
{
double newX = mouseEvent.getX() + deltaX;
double newY = mouseEvent.getY() + deltaY;
if (newX > 0 && newX < getScene().getWidth())
{
xProperty.set(newX);
}
if (newY > 0 && newY < getScene().getHeight())
{
yProperty.set(newY);
}
}
});
}
//JavaFx Accessor and mutator
}
public class CustomRectangle extends Rectangle
{
private DoubleProperty topRightX;
private DoubleProperty topRightY;
private DoubleProperty btmLeftX;
private DoubleProperty btmLeftY;
private DoubleProperty btmRightX;
private DoubleProperty btmRightY;
private DoubleProperty customeWidth;
private DoubleProperty customHeight;
public CustomRectangle()
{
super();
init();
}
public CustomRectangle(double x, double y, double width, double height)
{
super(x, y, width, height);
init();
}
public CustomRectangle(double width, double height, Paint fill)
{
super(width, height, fill);
init();
}
public CustomRectangle(double width, double height)
{
super(width, height);
init();
}
private void init()
{
topRightX = new SimpleDoubleProperty();
topRightY = new SimpleDoubleProperty();
btmLeftX = new SimpleDoubleProperty();
btmLeftY = new SimpleDoubleProperty();
btmRightX = new SimpleDoubleProperty();
btmRightY = new SimpleDoubleProperty();
topRightX.addListener((observable, oldValue, newValue) ->
{
this.setWidth(this.getWidth() + (newValue.doubleValue() - oldValue.doubleValue()));
});
topRightY.addListener((observable, oldValue, newValue) ->
{
this.setY(newValue.doubleValue());
this.setHeight(this.getHeight() - (newValue.doubleValue() - oldValue.doubleValue()));
});
btmLeftX.addListener((observable, oldValue, newValue) ->
{
this.setX(newValue.doubleValue());
this.setWidth(this.getWidth() - (newValue.doubleValue() - oldValue.doubleValue()));
});
btmLeftY.addListener((observable, oldValue, newValue) ->
{
this.setY(newValue.doubleValue());
this.setHeight(this.getHeight() + (newValue.doubleValue() - oldValue.doubleValue()));
});
btmRightX.addListener((observable, oldValue, newValue) ->
{
this.setWidth(this.getWidth() + (newValue.doubleValue() - oldValue.doubleValue()));
});
btmRightY.addListener((observable, oldValue, newValue) ->
{
this.setHeight(this.getHeight() + (newValue.doubleValue() - oldValue.doubleValue()));
});
this.xProperty().addListener((observable, oldValue, newValue) ->
{
btmLeftX.set(newValue.doubleValue());
topRightX.set(newValue.doubleValue() + this.getWidth());
btmRightX.set(newValue.doubleValue() + this.getWidth());
});
this.yProperty().addListener((observable, oldValue, newValue) ->
{
btmLeftY.set(newValue.doubleValue() + this.getHeight());
topRightY.set(newValue.doubleValue());
btmRightY.set(newValue.doubleValue() + this.getHeight());
});
this.widthProperty().addListener((observable, oldValue, newValue) ->
{
topRightX.set(this.getX() + (newValue.doubleValue() - oldValue.doubleValue()));
btmRightX.set(this.getX() + (newValue.doubleValue() - oldValue.doubleValue()));
});
this.heightProperty().addListener((observable, oldValue, newValue) ->
{
btmLeftY.set(this.getY() + (newValue.doubleValue() - oldValue.doubleValue()));
btmRightY.set(this.getY() + (newValue.doubleValue() - oldValue.doubleValue()));
});
}
//JavaFx Accessor and Mutator
}
public class FeatureHelper
{
private static double orgSceneX;
private static double orgSceneY;
private static double orgTranslateX;
private static double orgTranslateY;
public static CustomRectangle createDraggableRectangle(double x, double y, double width, double height,
boolean resizeImage)
{
CustomRectangle rect = new CustomRectangle(x, y, width, height);
// top left resize handle:
Handler topLeftHandle = new Handler(rect.topLeftXProperty(), rect.topLeftYProperty());
// top right resize handle:
Handler topRightHandle = new Handler(rect.topRightXProperty(), rect.topRightYProperty());
// bottom left resize handle:
Handler btmLeftHandle = new Handler(rect.btmLeftXProperty(), rect.btmLeftYProperty());
// bottom right resize handle:
Handler btmRightHandle = new Handler(rect.btmRightXProperty(), rect.btmRightYProperty());
// force circles to live in same parent as rectangle:
rect.parentProperty().addListener((obs, oldParent, newParent) ->
{
for (Circle c : Arrays.asList(topLeftHandle, topRightHandle, btmLeftHandle, btmRightHandle))
{
if (newParent != null)
{
((Pane) newParent).getChildren().add(c);
}
}
});
rect.setOnMousePressed(event ->
{
event.consume();
orgSceneX = event.getSceneX();
orgSceneY = event.getSceneY();
Node p = ((Node) (event.getSource()));
orgTranslateX = p.getTranslateX();
orgTranslateY = p.getTranslateY();
});
rect.setOnMouseDragged(event ->
{
double offsetX = event.getSceneX() - orgSceneX;
double offsetY = event.getSceneY() - orgSceneY;
double newTranslateX = orgTranslateX + offsetX;
double newTranslateY = orgTranslateY + offsetY;
Node p = ((Node) (event.getSource()));
p.setTranslateX(newTranslateX);
p.setTranslateY(newTranslateY);
for(Circle circle : Arrays.asList(topLeftHandle, topRightHandle, btmLeftHandle, btmRightHandle))
{
circle.setTranslateX(newTranslateX);
circle.setTranslateY(newTranslateY);
}
});
return rect;
}
}
You should have a look at this nice Example from karakullukcuhuseyin -->
https://github.com/karakullukcuhuseyin/JavaFX-ImageCropper
He extended the Rectangle Class as well and is using his custom Rectangle to select an area in an Image.

Lineargradient for Line in javafx

How can I use lineargradient for Line in Javafx?
I tried this:
LinearGradient lg = new LinearGradient(...);
line.setFill(lg);
....
It doesn't work.
Based on this answer, you need to specify the gradient on absolute coordinates.
Something like this will work for any given ones:
LinearGradient linearGradient = new LinearGradient(x1, y1, x2, y2, false, CycleMethod.REFLECT, new Stop(0,Color.RED),new Stop(1,Color.GREEN));
line.setStroke(linearGradient);
Based on this answer, you can create a simple app to test it how it works when you move the anchor nodes around the scene:
private final DoubleProperty x1 = new SimpleDoubleProperty();
public final double getX1() { return x1.get(); }
public final void setX1(double value) { x1.set(value); }
public final DoubleProperty x1Property() { return x1; }
private final DoubleProperty x2 = new SimpleDoubleProperty();
public final double getX2() { return x2.get(); }
public final void setX2(double value) { x2.set(value); }
public final DoubleProperty x2Property() { return x2; }
private final DoubleProperty y1 = new SimpleDoubleProperty();
public final double getY1() { return y1.get(); }
public final void setY1(double value) { y1.set(value); }
public final DoubleProperty y1Property() { return y1; }
private final DoubleProperty y2 = new SimpleDoubleProperty();
public final double getY2() { return y2.get(); }
public final void setY2(double value) { y2.set(value); }
public final DoubleProperty y2Property() { return y2; }
private Line line;
#Override
public void start(Stage primaryStage) {
Group group = new Group();
primaryStage.setScene(new Scene(group, 400, 400));
x1.set(100);
y1.set(50);
x2.set(200);
y2.set(300);
line = new Line(x1.get(), y1.get(), x2.get(), y2.get());
line.startXProperty().bind(x1);
line.startYProperty().bind(y1);
line.endXProperty().bind(x2);
line.endYProperty().bind(y2);
line.setStrokeWidth(12);
line.setMouseTransparent(true);
Anchor start = new Anchor(Color.BLUE, x1, y1);
Anchor end = new Anchor(Color.YELLOW, x2, y2);
group.getChildren().setAll(line, start, end);
x1Property().addListener(o -> updateLine());
x2Property().addListener(o -> updateLine());
y1Property().addListener(o -> updateLine());
y2Property().addListener(o -> updateLine());
updateLine();
primaryStage.show();
}
private void updateLine() {
LinearGradient linearGradient = new LinearGradient(x1.get(), y1.get(), x2.get(), y2.get(), false, CycleMethod.REFLECT, new Stop(0,Color.RED),new Stop(1,Color.GREEN));
line.setStroke(linearGradient);
}
private class Anchor extends Circle {
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);
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(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(mouseEvent -> {
getScene().setCursor(Cursor.HAND);
});
setOnMouseDragged(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(mouseEvent -> {
if (!mouseEvent.isPrimaryButtonDown()) {
getScene().setCursor(Cursor.HAND);
}
});
setOnMouseExited(mouseEvent -> {
if (!mouseEvent.isPrimaryButtonDown()) {
getScene().setCursor(Cursor.DEFAULT);
}
});
}
// records relative x and y co-ordinates.
private class Delta { double x, y; }
}
Use this
line.setStyle("-fx-background-color: linear-gradient(to right, #color_1, #color_2);");

JavaFx unable to execute javascript function

I am using JavaFX to embed browser. I am trying to run a javascript function addnum() from java class WebScale, but i am getting error.If i execute document.write() from webengine.executeScript() it is possible. But i cant call my function.
My code is as follow:
public class WebScale extends JApplet {
static ZoomingPane zoomingPane;
private static JFXPanel fxContainer;
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
final JFrame frame = new JFrame("Area Configurator");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JApplet applet = new WebScale();
applet.init();
frame.setContentPane(applet.getContentPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
applet.start();
frame.addComponentListener(new java.awt.event.ComponentAdapter() {
#Override
public void componentResized(java.awt.event.ComponentEvent evt) {
if (zoomingPane != null) {
zoomingPane.setZoomFactors((double)(frame.getWidth()/ 1280.0), (double)(frame.getHeight() / 800.0));
}
}
});
}
});
}
#Override
public void init() {
fxContainer = new JFXPanel();
fxContainer.setPreferredSize(new Dimension(800, 700));
add(fxContainer, BorderLayout.CENTER);
// create JavaFX scene
Platform.runLater(new Runnable() {
#Override
public void run() {
createScene();
}
});
}
private void createScene() {
WebView webView = new WebView();
zoomingPane = new ZoomingPane(webView);
BorderPane bp = new BorderPane();
bp.setCenter(zoomingPane);
fxContainer.setScene(new Scene(bp));
String strpath ;
strpath="file:///C:/Users/Priyanka/Desktop/FDASH/StationV3/Main.html";
final WebEngine engine = webView.getEngine();
engine.load(strpath);
engine.executeScript("addNum()");
}
private class ZoomingPane extends Pane {
Node content;
private final DoubleProperty zoomFactor = new SimpleDoubleProperty(1);
private double zoomFactory = 1.0;
private ZoomingPane(Node content) {
this.content = content;
getChildren().add(content);
final Scale scale = new Scale(1, 1);
content.getTransforms().add(scale);
zoomFactor.addListener(new ChangeListener<Number>() {
#Override
public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
scale.setX(newValue.doubleValue());
scale.setY(zoomFactory);
requestLayout();
}
});
}
#Override
protected void layoutChildren() {
Pos pos = Pos.TOP_LEFT;
double width = getWidth();
double height = getHeight();
double top = getInsets().getTop();
double right = getInsets().getRight();
double left = getInsets().getLeft();
double bottom = getInsets().getBottom();
double contentWidth = (width - left - right)/zoomFactor.get();
double contentHeight = (height - top - bottom)/zoomFactory;
layoutInArea(content, left, top,
contentWidth, contentHeight,
0, null,
pos.getHpos(),
pos.getVpos());
}
public final Double getZoomFactor() {
return zoomFactor.get();
}
public final void setZoomFactor(Double zoomFactor) {
this.zoomFactor.set(zoomFactor);
}
public final void setZoomFactors(Double zoomFactorx, Double Zoomfactory) {
this.zoomFactory = Zoomfactory;
this.zoomFactor.set(zoomFactorx);
}
public final DoubleProperty zoomFactorProperty() {
return zoomFactor;
}
}
}
I am getting the following error.
Exception in thread "JavaFX Application Thread" netscape.javascript.JSException: ReferenceError: Can't find variable: addNum
at com.sun.webkit.dom.JSObject.fwkMakeException(JSObject.java:128)
at com.sun.webkit.WebPage.twkExecuteScript(Native Method)
at com.sun.webkit.WebPage.executeScript(WebPage.java:1439)
at javafx.scene.web.WebEngine.executeScript(WebEngine.java:982)
at c.WebScale.createScene(WebScale.java:97)
at c.WebScale.access$0(WebScale.java:83)
at c.WebScale$2.run(WebScale.java:78)
at com.sun.javafx.application.PlatformImpl.lambda$null$174(PlatformImpl.java:295)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.application.PlatformImpl.lambda$runLater$175(PlatformImpl.java:294)
at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at com.sun.glass.ui.win.WinApplication.lambda$null$149(WinApplication.java:191)
at java.lang.Thread.run(Thread.java:745)
Assuming addNum() is defined in Main.html, the javascript hasn't been loaded at the time that you're calling it. You should add a listener so you can call your javascript once the page is fully loaded:
final WebEngine engine = webView.getEngine();
engine.getLoadWorker().stateProperty().addListener(
new ChangeListener<State>() {
public void changed(ObservableValue ov, State oldState, State newState) {
if (newState == State.SUCCEEDED) {
engine.executeScript("addNum()");
}
}
});
engine.load(strpath);

Resources