Rectangle with rounded corner as ProgressBar using Path - javafx

i need to draw rectangle rounded corner with progressbar in javafx.
i already did some code which draw rectangle with corner.
but how do i show progress effect.
Following are the example which I am trying to imitate.
http://jsfiddle.net/m1erickson/P2qTq/
there is any other way to do this. guide me thank you.
void drawLine(double x1,double y1,double x2,double y2) {
MoveTo move= new MoveTo(x1, y1);
path.getElements().add(move);
LineTo line= new LineTo(x2,y2);
path.getElements().add(line);
path.setStroke(Paint.valueOf("#000000"));
}
void drawCorner(double x0,double y0,double x1,double y1) {
QuadCurveTo quadCurveTo=new QuadCurveTo();
quadCurveTo.setX(x0);
quadCurveTo.setY(y0);
quadCurveTo.setControlX(x1);
quadCurveTo.setControlY(y1);
path.getElements().add(quadCurveTo);
}
Group group=new Group();
double angleTRX=offsetX+horizontalLength+cornerRadius;
double angleTRY=offsetY+cornerRadius;
double TRCornerX=offsetX+horizontalLength+cornerRadius;
double TRCornerY=offsetY;
double bottomRY=angleTRY+verticalLength;
//draw top line
drawLine(offsetX, offsetY, offsetX + horizontalLength, offsetY);
//draw top right corner
drawCorner(angleTRX, offsetY+cornerRadius, TRCornerX, offsetY);
//draw right line
drawLine(angleTRX,angleTRY, angleTRX,bottomRY);
//draw bottom right corner
drawCorner(offsetX+horizontalLength,offsetY+verticalLength+cornerRadius*2,
offsetX+horizontalLength+cornerRadius,offsetY+verticalLength+cornerRadius*2);
//draw bottom line
drawLine(offsetX+horizontalLength,offsetY+verticalLength+cornerRadius*2,
offsetX,offsetY+verticalLength+cornerRadius*2);
//draw left bottom corner
drawCorner(offsetX-cornerRadius,offsetY+verticalLength+cornerRadius,
offsetX-cornerRadius,offsetY+verticalLength+cornerRadius*2);
//draw left line
drawLine(offsetX-cornerRadius,offsetY+verticalLength+cornerRadius,
offsetX-cornerRadius,offsetY+cornerRadius);
//draw top left corner
drawCorner(offsetX,offsetY,offsetX-cornerRadius,offsetY);
group.getChildren().add(path);
--> Update <--
i found another way to do it. using Rectangle with stroke-offset.
but still facing one issue stroke are starting from clockwise.
Rectangle rectangle= new Rectangle();
rectangle.setX(offsetX);
rectangle.setY(offsetY);
rectangle.setArcHeight(cornerRadius);
rectangle.setArcWidth(cornerRadius);
rectangle.setWidth(200);
rectangle.setHeight(200);
rectangle.setStroke(Paint.valueOf("#000000"));
rectangle.setFill(Paint.valueOf("#ffffff"));
rectangle.setStrokeWidth(5);
rectangle.setStrokeMiterLimit(5);
rectangle.setSmooth(true);
rectangle.setStrokeLineCap(StrokeLineCap.ROUND);
rectangle.setStrokeLineJoin(StrokeLineJoin.ROUND);
double length=rectangle.getWidth()*2+rectangle.getHeight()*2;
//set stroke dash length
rectangle.getStrokeDashArray().addAll(length);
//display empty stroke/border
rectangle.setStrokeDashOffset(length);
group.getChildren().add(rectangle);
container.add(group,0,0);
slider.valueProperty().addListener((observable, oldValue, newValue) -> {
rectangle.setStrokeDashOffset(length-(length*(newValue.doubleValue()/100)));
});
if you know better solution then please let me know.

i solved it using Rectangle and stroke-dash-offset. this might help others and save its valuable time.
Rectangle rectangle= new Rectangle();
rectangle.setX(offsetX);
rectangle.setY(offsetY);
rectangle.setWidth(200);
rectangle.setHeight(200);
rectangle.setStroke(Paint.valueOf("#000000"));
rectangle.setFill(Color.TRANSPARENT);
rectangle.setStrokeWidth(5);
rectangle.setStrokeMiterLimit(5);
rectangle.setSmooth(true);
rectangle.setStrokeLineCap(StrokeLineCap.ROUND);
// rectangle.setStrokeLineJoin(StrokeLineJoin.ROUND);
double length=rectangle.getWidth()*2+rectangle.getHeight()*2;
//set stroke dash length
rectangle.getStrokeDashArray().addAll(length);
//display empty stroke/border
rectangle.setStrokeDashOffset(length);
group.getChildren().add(rectangle);
container.add(group,0,0);
slider.valueProperty().addListener((observable, oldValue, newValue) -> {
double offset=length-((length*(newValue.doubleValue()/100)));
rectangle.setStrokeDashOffset(-offset);
});

Related

Change rotation base of a javafx line

Is there a way to rotate a line but instead of rotating from its center it rotates from its left end. I'm creating a clock but with rotatetransition the lines rotate from their center.
Codigo de rotacion:
Line contFH = new Line();
contFH.setPrefSize(150, 20);
contFH.setRotate(90);
RotateTransition r1 = new RotateTransition(Duration.seconds(10),contFH);
r1.setByAngle(360);
r1.setInterpolator(Interpolator.LINEAR);
r1.play();
I made the following changes and now I get the result I want, but this little problem is generated:
the line moves from its axis out of the small circle in the center.
Is there a way to keep it from moving on its axis when rotating?
Thanks for the help!
Create a Rotate and add it to the line's transformations. Then animate the angle:
Line contFH = new Line();
// This doesn't compile, not sure what you're doing here:
// contFH.setPrefSize(150, 20);
//contFH.setRotate(90);
Rotate rotate = new Rotate(90, contFH.getStartX(), contFH.getStartY());
contFH.getTransforms().add(rotate);
Timeline timeline = new Timeline(
new KeyFrame(Duration.ZERO, new KeyValue(rotate.angleProperty(), 90)),
new KeyFrame(Duration.seconds(10), new KeyValue(rotate.angleProperty(), 450)
);
timeline.play();

JavaFX 3D PerspectiveCamera affects drag position of a node

I have been having issues trying to move nodes in JavaFX within a 3D Scene. The issue is that I want the mouse to stay at the position within the node I've clicked, i.e., center. With a PerspectiveCamera it will alter the position. I've tried a couple of different methods and haven't had any luck. 2D Rectangles, and 3D Boxes(without a camera) work perfectly, but once a PerspectiveCamera is added, regardless of true/false parameter, I have issues.
I am wondering if this a bug that should be reported, or if there is some way to get another the perspective affecting the moving of nodes
public class Move extends Application {
double x0,xDiff;
double y0,yDiff;
#Override
public void start(Stage primaryStage) {
Box b = new Box(100,100,1);
b.setLayoutX(0);
b.setLayoutY(0);
// b.setTranslateZ(20000);
Pane root = new Pane();
root.getChildren().add(b);
PhongMaterial p = new PhongMaterial();
p.setDiffuseColor(Color.RED);
b.setMaterial(p);
Scene scene = new Scene(root, 2000, 1250,true);
PerspectiveCamera camera = new PerspectiveCamera(true);
camera.setTranslateZ(-1000);
camera.setFarClip(2000);
scene.setCamera(camera);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
b.setOnMousePressed(event
->{
x0 = event.getSceneX();
y0 = event.getSceneY();
event.consume();
});
b.setOnMouseDragged(event
->{
xDiff = event.getSceneX() - x0;
yDiff = event.getSceneY() - y0;
b.setLayoutX(b.getLayoutX() + xDiff);
b.setLayoutY(b.getLayoutY() + yDiff);
x0 = event.getSceneX();
y0 = event.getSceneY();
});
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
I'm using Java 8 update 91 or 181 (it seems netbeans puts the default at 91, but I have 181 as well)
JavaFX Moving 3D objects with mouse on a virtual plane
I also found this post, and had tried the answer and it seemed like it also had some issues, but seemed much better(except it it was hard to test with the additional code of spinning the node when dragging, so if you drag off the sphere it rotates instead).
Thank you very much
EDIT: After trying to go back to how I originally dragged, I was able to get to a point where I could get the mouse cursor to stay in the middle, but I am trying to figure out how to get the exact position.
b.setLayoutX(b.getLayoutX() + (event.getX()));
b.setLayoutY(b.getLayoutY() + (event.getY()));
will give me the center of the node.
I originally used similar code to this for 2D, but was having issues with 3D, which I am assuming was due to the differences of 0,0 top-left corner, vs 0,0,0 in the center.
The code for 2D was something along the lines of
b.setLayoutX(b.getLayoutX() + (event.getX()-b.getMinX()));
b.setLayoutY(b.getLayoutY() + (event.getY()-b.getMinY()));
Essentially, from what I see when I set the original layout + the event position it just moves the center/top-left to the coordinates of the mouse event, so I would try to get the difference between the origin of the node and the mouseEvent, which is what event.getX() does, and try to figure out the difference to move, which is what the event.getSceneX() - x0 is for. I tried doing it without the Scene X/Y but it doesn't seem to work properly, but I'm not sure if using the SceneX/Y is what I should be doing.

JavaFX - Get Coordinates of Node Relative to its Parent

I am making a simple graphical interface for saving previously generated images. All images come to me square but I want to allow for some cropping functionality (more precisely cutting off equal parts from the bottom and top of the image). I want to do this by allowing the user to drag a shaded region over the image which will tell the user that this region will be cropped out. See the below image for details. To enable this drag functionality I have added small triangles that I want the user to drag which in turn will move the shaded regions about. However the coordinates for the triangles are all weird and seem nonsensical. Therefor I was wondering what the best way is to get the coordinates of the triangles in relation to the ImageView (or their first common parent node) in terms of ImageView-side-lengths. So if the triangle is in the center its coordinates are [0.5, 0.5] for instance.
The Image view will be moving around inside the scene and will also be changing size so it is vital that I can get the coordinates relative to not only the ImageView but also to the size of the ImageView.
Here is also the surrounding hierarchy of nodes if that helps. The Polygons are the triangles and the regions are the rectangles.
Thanks for all forms of help!
Node.getBoundsInParent returns the bounds of a node in it's parent coordinates. E.g. polygon.getBoundsInParent() would return the bounds in the VBox.
If you need to "go up" one additional step, you can use parent.localToParent to do this. vBox.localToParent(boundsInVbox) returns the bounds in the coordinate system of the AnchorPane.
To get values relative to the size of the image, you simply need to divide by it's size.
The following example only allows you to move the cover regions to in one direction and does not check, if the regions intersect, but it should be sufficient to demonstrate the approach.
The interesting part is the event handler of the button. It restricts the viewport of the second image to the part of the first image that isn't covered.
private static void setSideAnchors(Node node) {
AnchorPane.setLeftAnchor(node, 0d);
AnchorPane.setRightAnchor(node, 0d);
}
#Override
public void start(Stage primaryStage) {
// create covering area
Region topRegion = new Region();
topRegion.setStyle("-fx-background-color: white;");
Polygon topArrow = new Polygon(0, 0, 20, 0, 10, 20);
topArrow.setFill(Color.WHITE);
VBox top = new VBox(topRegion, topArrow);
top.setAlignment(Pos.TOP_CENTER);
topArrow.setOnMouseClicked(evt -> {
topRegion.setPrefHeight(topRegion.getPrefHeight() + 10);
});
// create bottom covering area
Region bottomRegion = new Region();
bottomRegion.setStyle("-fx-background-color: white;");
Polygon bottomArrow = new Polygon(0, 20, 20, 20, 10, 0);
bottomArrow.setFill(Color.WHITE);
VBox bottom = new VBox(bottomArrow, bottomRegion);
bottom.setAlignment(Pos.BOTTOM_CENTER);
bottomArrow.setOnMouseClicked(evt -> {
bottomRegion.setPrefHeight(bottomRegion.getPrefHeight() + 10);
});
Image image = new Image("https://upload.wikimedia.org/wikipedia/commons/thumb/e/ec/Mona_Lisa%2C_by_Leonardo_da_Vinci%2C_from_C2RMF_retouched.jpg/402px-Mona_Lisa%2C_by_Leonardo_da_Vinci%2C_from_C2RMF_retouched.jpg");
ImageView imageView = new ImageView(image);
setSideAnchors(top);
setSideAnchors(bottom);
setSideAnchors(imageView);
AnchorPane.setTopAnchor(top, 0d);
AnchorPane.setBottomAnchor(bottom, 0d);
AnchorPane.setTopAnchor(imageView, 0d);
AnchorPane.setBottomAnchor(imageView, 0d);
AnchorPane container = new AnchorPane(imageView, top, bottom);
ImageView imageViewRestricted = new ImageView(image);
Button button = new Button("restrict");
button.setOnAction(evt -> {
// determine bouns of Regions in AnchorPane
Bounds topBounds = top.localToParent(topRegion.getBoundsInParent());
Bounds bottomBounds = bottom.localToParent(bottomRegion.getBoundsInParent());
// set viewport accordingly
imageViewRestricted.setViewport(new Rectangle2D(
0,
topBounds.getMaxY(),
image.getWidth(),
bottomBounds.getMinY() - topBounds.getMaxY()));
});
HBox root = new HBox(container, button, imageViewRestricted);
root.setFillHeight(false);
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}

writing a gauge widget using 2 images in qt

I'm writing a gauge widget, using QT, that is constructed from 2 separate images, one as background and the other as Needle. I reimplement paintEvent function as follow:
void myGaugeWidget::paintEvent(QPaintEvent *pe)
{
QPainter painter(this);
QPixmap bkgImage(bkgImgPath);
painter.drawPixmap(0, 0, width(), height(), bkgImage);
const double thetaDeg = 30.0;
QPixmap needle(needles[i].imgPath);
int needleWidth = 200;
int needleHeight = 200;
int anchorX = 20;
int anchorY = 30;
const int centerX = width()/2;
const int centerY = height()/2;
QTransform rm = QTransform().translate(-anchorX,- anchorY).rotate(thetaDeg).translate(centerX,centerY);
needle = needle.transformed(rm);
painter.drawPixmap(0,0, needle);
}
this code rotates my needle correctly but its position is not correct.
can anybody help me?
thanks.
This most likely would depend on your images and widget size. I have tried your code and it seems to me that QTransform().translate() is not doing anything in a QPixmap. I tried to give extreme values for translate() and removed rotate() - the image does not move.
I already have have my own implementation for a gauge. This is with painter transformation instead of the image. My images are of dimensions:
Gauge Background: 252x252 (there is some external blurring effects around the circle boundaries, making the background image larger than it seems)
Needle: 7x72 ( the image dimensions wrap around the boundaries of the needle itself)
Needle roation center (with respect to the background): 126, 126 (divide background size by 2)
The needle image points upward
For this setup, here is my paintEvent() with some explanations:
void myGaugeWidget::paintEvent(QPaintEvent *)
{
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
//draw the background which is same size as the widget.
painter.drawPixmap(0, 0, bg.width(), bg.height(), bg);
//Calculate the angle of rotation.
//The gauge I am using has a cutout angle of 120 degrees at the bottom (symmetric)
float needleAngle = -120/*offset for start rotation*/ + ((value-minValue)*240/*total sweep of the gauge*//(maxValue-minValue));
painter.save();
//translate the painter to the roation center and then perform the rotation
painter.translate(126, 126);
painter.rotate(needleAngle);
//translate the rotated canvas to adjust for the height of the needle.
//If you don't do this, your needle's tip will be at the rotation center
painter.translate(0, -72);
//draw the needle and adjust for the width with the x value
painter.drawPixmap(-needle.width()/2, 0, needle.width(), needle.height(), needle);
painter.restore();
}

How to align to the a rectangle to the left of a QGraphicsView without scaling the scene?

Here is what I want to do.
I need to draw in a QGraphicsView a series of rectangles that are aligned left and right. By this I mean that if rectangle i has posion (0,y) rectangle i+1 needs to have position (0,max) where max is such that the right side of the rectangle "touches" the right side of the QGraphicsView.
When the window is resized I need to recalculate the value of max such that the rectangle is always touching the right side of the screen.
Here is how I add my scene (this references a class that inherits QGraphicsView)
scene = new QGraphicsScene(this);
this->setScene(scene);
this->setAlignment(Qt::AlignTop|Qt::AlignLeft);
To add a rectangle that touches the left border I add it a (0,yvalue,width,height).
How do I calculate the value of X so that that the rectangle will touch the right border?
Ok. So this should go in the resize event of the QGraphicsView. But this does what I wanted:
void AView::resized(){
scene->clear();
QSize newsize = this->size();
qreal w = newsize.width()*.99;
qreal h = newsize.height()*.99;
this->setSceneRect(0,0,w,h);
scene->setSceneRect(0,0,w,h);
qreal rectwidth = 100;
qreal newx = w - rectwidth;
left = new QGraphicsRectItem(0,0,rectwidth,100);
left->setBrush(QBrush(Qt::red));
right = new QGraphicsRectItem(newx,100,rectwidth,100);
right->setBrush(QColor(Qt::blue));
scene->addItem(left);
scene->addItem(right);
}
In this way the blue rectangle is pretty much always at the border. Aslo there is no stretching of the image. There is a gap between the right borders which increases due to the window resizing, but it seems that the size returned by this->size() is slightly larger than the "white area" that you see on screen. Adding the .99 gives a much better results from my experiments.
This little example should calculate the shift and move all items from a selection or list of items, based on alignment you desire (I showed right, but let, center, top, bottom would be the same).
QRectF refRect = scene()->sceneRect();
QList<QGraphicsItem*> sel = allItemsYouWantAligned; // scene()->selectedItems(); for example
foreach(QGraphicsItem* selItem, sel)
{
qreal dx = 0, dy = 0;
QRectF itemRect = selItem->mapToScene(selItem->boundingRect()).boundingRect();
if(align_right)
dx = refRect.right() - itemRect.right();
else
.. calculate either dx or dy on how you want to align
selItem->moveBy(dx, dy);
}
Re-reading the question - I see you don't really need to move rectangle, but create new larger and larger rectangles.
Your solution is simple enough. If you want to resize instead of move -
you would have to setRect() on your items by dx increment:
QRectF r = selItem->rect();
r.setWidth(r.width + dx);
selItem->setRect(r);

Resources