When zooming with JavaFX, items move weirdly - javafx

I have a problem with zooming using javaFX and zooming. The items are wrapped in a group which is then scaled by the ZoomUtil. One node is animated to show the weird effect resulting: The Rectangles should stay within their original place and not constantly move around the frame.
I have no idea what causes this behaviour and tried many different transform settings and such but were not successful.
public class Test extends Application {
#Override
public void start(Stage primaryStage) {
List<Rectangle> rectangles = new ArrayList<>(5);
Color color = new Color(random(), random(), random(), 1);
Rectangle r = new Rectangle(200, 140,color);
r.relocate(-500,-500);
rectangles.add(r);
r = new Rectangle(200,140,color);
r.relocate(500, -500);
rectangles.add(r);
r = new Rectangle(200,140,color);
r.relocate(500, 500);
rectangles.add(r);
r = new Rectangle(200,140,color);
r.relocate(0, 0);
rectangles.add(r);
r = new Rectangle(200,140,color);
r.relocate(-500, 500);
rectangles.add(r);
Timeline timeline = new Timeline();
Node circle = r;
timeline.getKeyFrames().addAll(
new KeyFrame(Duration.seconds(10), // set start position at 0
new KeyValue(circle.translateXProperty(), random() * 8000),
new KeyValue(circle.translateYProperty(), random() * 6000)
),
new KeyFrame(new Duration(80000), // set end position at 40s
new KeyValue(circle.translateXProperty(), random() * 8000),
new KeyValue(circle.translateYProperty(), random() * 6000)
)
);
Group group = new Group();
group.setManaged(false);
group.getChildren().addAll(rectangles);
StackPane pane = new StackPane();
pane.setPrefWidth(500);
pane.setPrefHeight(500);
Scene scene = new Scene(pane);
pane.getChildren().add(group);
pane.setOnScroll(event -> ZoomUtil.zoom(group, event));
primaryStage.setScene(scene);
primaryStage.setWidth(500);
primaryStage.setHeight(500);
primaryStage.show();
timeline.play();
}
}
.
public class ZoomUtil {
/** Allow to zoom/scale any node with pivot at scene (x,y) coordinates.
*
* #param node
* #param x
* #param y
*/
public static void zoom(Node node, double factor, double x, double y) {
double oldScale = node.getScaleX();
double scale = oldScale * factor;
if (scale < 0.05) scale = 0.05;
if (scale > 50) scale = 50;
node.setScaleX(scale);
node.setScaleY(scale);
double f = (scale / oldScale) - 1;
/* Move view by translational difference */
Bounds bounds = node.localToScene(node.getBoundsInLocal());
double dx = (x - (bounds.getWidth() / 2 + bounds.getMinX()));
double dy = (y - (bounds.getHeight() / 2 + bounds.getMinY()));
node.setTranslateX(node.getTranslateX() - f * dx);
node.setTranslateY(node.getTranslateY() - f * dy);
}
public static void zoom(Node node, ScrollEvent event) {
zoom(node, Math.pow(1.002, event.getDeltaY()), event.getSceneX(), event.getSceneY());
}
public static void zoom(Node node, ZoomEvent event) {
zoom(node, event.getZoomFactor(), event.getSceneX(), event.getSceneY());
}
}

Related

how to morph shapes from circle to rectangle in javafx

I want to morph shape from circle to rectangle in javafx using javafx scene shapes.
Circle c = new Circle();
Rectangle r = new Rectangle();
You can change the arcWidth/arcHeight properties of the Rectangle between 0 and the rectangle width/height as one way of doing the transformation between circle and rectangle:
#Override
public void start(Stage primaryStage) throws Exception {
final double size = 100;
Slider slider = new Slider(0, size, 0);
Rectangle rect = new Rectangle(size, size);
rect.arcHeightProperty().bind(slider.valueProperty());
rect.arcWidthProperty().bind(slider.valueProperty());
VBox vbox = new VBox(slider, rect);
Scene scene = new Scene(vbox);
primaryStage.setScene(scene);
primaryStage.show();
}
Edit:
For arbitrary regular polygons, you need could use a path with ArcTo instead. The transformation in the following example looks different to the one resulting from the code above. (For the mathematical details of the radius calculation, see https://en.wikipedia.org/wiki/Circular_segment)
/**
* Create a regular "polygon" that can be transformed to a circle using the
* circleliness property
*
* #param sides the number of sides of the polygon
* #param centerX the x coordinate of the center of mass of the polygon
* #param centerY the y coordinate of the center of mass of the polygon
* #param radius the distance of the corners of the polygon from the center
* of mass
* #param circeliness a property indiating approximately straight lines (value =
* 0) or circle (value = 1)
* #return The path
*/
public static Path createPolygon(int sides, final double centerX, final double centerY, final double radius,
final DoubleProperty circeliness) {
if (sides < 3 || radius <= 0) {
throw new IllegalArgumentException();
}
Path path = new Path(new MoveTo(centerX, centerY + radius));
final double angleStep = Math.PI * 2 / sides;
// side length
final double c = 2 * radius * Math.sin(0.5 * angleStep);
// max value for radius -> circle
final double hMax = radius * (1 - Math.cos(0.5 * angleStep));
final DoubleBinding radiusBinding = Bindings.createDoubleBinding(() -> {
double h = hMax * circeliness.get();
double result = c * c / (8 * h) + 0.5 * h;
return Math.min(result, 500 * radius); // limit result, since for too large radii ArcTo stops working
}, circeliness);
for (int i = 1; i <= sides; i++) {
double angle = angleStep * i;
ArcTo arc = new ArcTo();
arc.setX(centerX + radius * Math.sin(angle));
arc.setY(centerY + radius * Math.cos(angle));
arc.setLargeArcFlag(false);
arc.radiusXProperty().bind(radiusBinding);
arc.radiusYProperty().bind(radiusBinding);
path.getElements().add(arc);
}
path.getElements().add(new ClosePath());
return path;
}
#Override
public void start(Stage primaryStage) throws Exception {
Slider slider = new Slider(0, 1, 0);
StackPane layout = new StackPane(createPolygon(6, 0, 0, 50, slider.valueProperty()));
Scene scene = new Scene(new VBox(slider, layout), 300, 250);
primaryStage.setScene(scene);
primaryStage.show();
}

How can I get the center coordinates of a rectangle in javafx?

do anyone has an idea on how to get the center-coordinates of an rectangle in javafx?
Is there a method or an algorithm for that, couse I want to know the center-position while the rectangle is rotating and moving.
I couldn't find a solution on the web so I hope one of you can help me.
You can calculate the center of a rectangle like this:
center.x = position.x + width / 2
center.y = position.y + height / 2
Or if you represent your rectangle as 2 points (two opposite corners) you would have to use the following formula:
p0
+-------+
| |
| |
+-------+
p1
center.x = (p0.x + p1.x) / 2
center.y = (p0.y + p1.y) / 2
Also:
A quick google yields How to find the Center Coordinate of Rectangle? as first result...
Use the localToParent method to convert the center of the rectangle to the coordinate system of the parent.
The center of the Rectangle in local coordinates is
x = rect.x + rect.width/2
y = rect.y + rect.height/2
Example
#Override
public void start(Stage primaryStage) throws Exception {
Rectangle rect = new Rectangle(50, 50, 100, 100);
Translate translate = new Translate();
Rotate rotate = new Rotate(0, 0, 0);
rect.getTransforms().addAll(translate, rotate);
Circle circle = new Circle(5, Color.RED);
AnimationTimer timer = new AnimationTimer() {
#Override
public void handle(long now) {
double d = now / 5_000_000_000d;
rotate.setAngle(d * 360);
translate.setX((d % 2) * 300);
translate.setY((d % 3) * 150);
// set circle center to coordinates of rect's center
Point2D center = rect.localToParent(rect.getX() + 0.5 * rect.getWidth(),
rect.getY() + 0.5 * rect.getHeight());
circle.setCenterX(center.getX());
circle.setCenterY(center.getY());
}
};
Pane root = new Pane(rect, circle);
timer.start();
Scene scene = new Scene(root, 800, 800);
primaryStage.setScene(scene);
primaryStage.show();
}

Make chips Selectable on mouse click event in wafermap jfree chart (javafx application)

I have created wafermap chart. I want to make chips(die) in wafer selectable on mouse click and insert labels as well lines from one chip to another. Anyone expert in jfree chart?
wafermap chart
Here are the basic pieces for a tooltip listener for wafer maps, which is a form of selecting the die. Add the following to WaferMapPlot:
public String findChipAtPoint(double x, double y, Rectangle2D plotArea){
double[] xValues = this.getChipXValues(plotArea, dataset.getMaxChipX()
+ 2, dataset.getChipSpace());
double startX = xValues[1];
double chipWidth = xValues[0];
int ychips = this.dataset.getMaxChipY()+ 2;
double[] yValues = this.getChipYValues(plotArea, ychips,
dataset.getChipSpace());
double startY = yValues[1];
double chipHeight = yValues[0];
double chipSpace = dataset.getChipSpace();
int chipX = (int)Math.floor((x - startX + chipWidth + chipSpace) /
(chipWidth + chipSpace));
int chipY = (int)Math.floor((y - startY + chipHeight + chipSpace) /
(chipHeight + chipSpace));
chipX = chipX - dataset.getXOffset() - 1;
chipY = ychips - chipY - dataset.getYOffset() - 1;
StringBuilder sb = new StringBuilder("(");
Number value = dataset.getChipValue(chipX, chipY);
if (value instanceof Double)
value = value.intValue();
sb.append(chipX).append(",").append(chipY).append(") ").append(
(value == null) ? "" : value.toString());
return sb.toString();
}
Then make a subclass of ChartPanel that will be the listener:
public class WaferMapChartPanel extends ChartPanel {
WaferMapPlot waferPlot = null;
WaferMapDataset dataSet = null;
public WaferMapChartPanel(JFreeChart chart){
super(chart);
waferPlot = (WaferMapPlot)chart.getPlot();
if (waferPlot != null)
dataSet = waferPlot.getDataset();
}
/**
* Returns a string for the tooltip.
* #param e the mouse event.
* #return A tool tip or <code>null</code> if no tooltip is available.
*/
#Override
public String getToolTipText(MouseEvent e) {
if (waferPlot != null){
Object source = e.getSource();
if (source instanceof WaferMapChartPanel){
WaferMapChartPanel chartSource= (WaferMapChartPanel)e.getSource();
Rectangle2D plotArea = chartSource.getChartRenderingInfo().getPlotInfo().getPlotArea();
Insets insets = this.getInsets();
double x = (e.getX() - insets.left) / this.getScaleX();
double y = (e.getY() - insets.top) / this.getScaleY();
return waferPlot.findChipAtPoint(x, y, plotArea);
}
}
return "";
}
}
This creates a tooltip of the die's x,y and bin or whatever value you are using instead of bin.

Draw a semi ring - JavaFX

I would like to know how to draw a semi circle in JavaFX. I tried to use Shape and QuadCurve but I couldn't make a perfect semicircle.
Here is a picture of what I'm trying to draw :
The picture you linked is actually a semi-ring. You can get it in JavaFX by drawing nested 2 arcs and some lines. But my preferred way is to use the Path.
public class SemiDemo extends Application {
#Override
public void start(Stage primaryStage) {
Group root = new Group();
root.getChildren().add(drawSemiRing(120, 120, 100, 50, Color.LIGHTGREEN, Color.DARKGREEN));
root.getChildren().add(drawSemiRing(350, 350, 200, 30, Color.LIGHTSKYBLUE, Color.DARKBLUE));
Scene scene = new Scene(root, 300, 250);
primaryStage.setScene(scene);
primaryStage.show();
}
private Path drawSemiRing(double centerX, double centerY, double radius, double innerRadius, Color bgColor, Color strkColor) {
Path path = new Path();
path.setFill(bgColor);
path.setStroke(strkColor);
path.setFillRule(FillRule.EVEN_ODD);
MoveTo moveTo = new MoveTo();
moveTo.setX(centerX + innerRadius);
moveTo.setY(centerY);
ArcTo arcToInner = new ArcTo();
arcToInner.setX(centerX - innerRadius);
arcToInner.setY(centerY);
arcToInner.setRadiusX(innerRadius);
arcToInner.setRadiusY(innerRadius);
MoveTo moveTo2 = new MoveTo();
moveTo2.setX(centerX + innerRadius);
moveTo2.setY(centerY);
HLineTo hLineToRightLeg = new HLineTo();
hLineToRightLeg.setX(centerX + radius);
ArcTo arcTo = new ArcTo();
arcTo.setX(centerX - radius);
arcTo.setY(centerY);
arcTo.setRadiusX(radius);
arcTo.setRadiusY(radius);
HLineTo hLineToLeftLeg = new HLineTo();
hLineToLeftLeg.setX(centerX - innerRadius);
path.getElements().add(moveTo);
path.getElements().add(arcToInner);
path.getElements().add(moveTo2);
path.getElements().add(hLineToRightLeg);
path.getElements().add(arcTo);
path.getElements().add(hLineToLeftLeg);
return path;
}
public static void main(String[] args) {
launch(args);
}
}
Refer to Shape API of JavaFX for more info about the shapes used in the code.
Screenshot:
Suggestions:
If you don't need a full outlining path, you can just use an Arc.
If you don't need the arc filled and just want to trace the outline path of the arc, then set the fill of the arc to null.
If you want the outline path of the arc thick, then set the stroke parameters on the arc.
If you need the a thick arc which is also outlined, then it is best to define a full arc as in Uluk's answer.
Sample code:
import javafx.application.Application;
import javafx.scene.*;
import javafx.scene.paint.Color;
import javafx.scene.shape.*;
import javafx.stage.Stage;
public class SemiCircleSample extends Application {
#Override public void start(Stage stage) {
Arc arc = new Arc(50, 50, 25, 25, 0, 180);
arc.setType(ArcType.OPEN);
arc.setStrokeWidth(10);
arc.setStroke(Color.CORAL);
arc.setStrokeType(StrokeType.INSIDE);
arc.setFill(null);
stage.setScene(new Scene(new Group(arc), 100, 80));
stage.show();
}
public static void main(String[] args) { launch(args); }
}
As an experiment, I tried to do the same thing on a Canvas. This is what I came up with, making use of a RadialGradient and the function GraphicsContext.fillArc:
/**
*
* #param x Coordinate x of the centre of the arc
* #param y Coordinate y of the centre of the arc
* #param outer Outer radius of the arc
* #param innerPercentage Inner radius of the arc, from 0 to 1 (as percentage)
* #param arcStartAngle Start angle of the arc, in degrees
* #param arcExtent Extent of the arc, in degrees
*/
private void drawSemiCircle(float x, float y, float outer, float innerPercentage, float arcStartAngle, float arcExtent) {
RadialGradient rg = new RadialGradient(
0,
0,
x,
y,
outer,
false,
CycleMethod.NO_CYCLE,
new Stop((innerPercentage + (.0 * innerPercentage)), Color.TRANSPARENT),
new Stop((innerPercentage + (.1 * innerPercentage)), Color.RED),
new Stop((innerPercentage + (.6 * innerPercentage)), Color.YELLOW),
new Stop((innerPercentage + (1 * innerPercentage)), Color.GREEN)
);
gc.setFill(rg);
gc.fillArc(
x - outer,
y - outer,
outer * 2,
outer * 2,
arcStartAngle,
arcExtent,
ArcType.ROUND
);
}
Key points here are the arc type as ArcType.ROUND and the use of Color.TRANSPARENT as the first color.
Then it can be used something along the line:
drawSemiCircle(100, 100, 100, .5f, -45, 270);
It's not a perfect solution but it worked for me.
Path.arcTo() the parameter SweepAngle refers to the rotation degree, if sweepAngle is positive the arc is clockwise, if sweepAngle is negative the arc is counterclockwise.
This code is used in my production environment, it draws a semi-circle ring using a bitmap image, the path goes clockwise on the outer radius, and counter-clockwise on the inner radius:
drawpercent = 0.85; //this draws a semi ring to 85% you can change it using your code.
DegreesStart = -90;
DegreesRotation = 180;
radiusPathRectF = new android.graphics.RectF((float)CentreX - (float)Radius, (float)CentreY - (float)Radius, (float)CentreX + (float)Radius, (float)CentreY + (float)Radius);
innerradiusPathRectF = new android.graphics.RectF((float)CentreX - (float)InnerRadius, (float)CentreY - (float)InnerRadius, (float)CentreX + (float)InnerRadius, (float)CentreY + (float)InnerRadius);
Path p = new Path(); //TODO put this outside your draw() function, you should never have a "new" keyword inside a fast loop.
degrees = (360 + (DegreesStart)) % 360;
radians = (360 - degrees + 90) * Math.PI / 180.0;
//radians = Math.toRadians(DegreesStart);
int XstartOuter = (int)Math.round((Math.cos(radians) * Radius + CentreX));
int YstartOuter = (int)Math.round((Math.sin(-radians)* Radius + CentreY));
int XstartInner = (int)Math.round((Math.cos(radians) * InnerRadius + CentreX));
int YstartInner = (int)Math.round((Math.sin(-radians) * InnerRadius + CentreY));
degrees = (360 + (DegreesStart + drawpercent * DegreesRotation)) % 360;
//radians = degrees * Math.PI / 180.0;
radians = (360 - degrees + 90) * Math.PI / 180.0;
//radians = Math.toRadians(DegreesStart + drawpercent * DegreesRotation);
int XendOuter = (int)Math.round((Math.cos(radians) * Radius + CentreX));
int YendOuter = (int)Math.round((Math.sin(-radians) * Radius + CentreY));
int XendInner = (int)Math.round((Math.cos(radians) * InnerRadius + CentreX));
int YendInner = (int)Math.round((Math.sin(-radians) * InnerRadius + CentreY));
//draw a path outlining the semi-circle ring.
p.moveTo(XstartInner, YstartInner);
p.lineTo(XstartOuter, YstartOuter);
p.arcTo(radiusPathRectF, (float)DegreesStart - (float)90, (float)drawpercent * (float)DegreesRotation);
p.lineTo(XendInner, YendInner);
p.arcTo(innerradiusPathRectF, (float)degrees - (float)90, -1 * (float)drawpercent * (float)DegreesRotation);
p.close();
g.clipPath(p);
g.drawBitmap(bitmapCircularBarImage, bitmapRect0, bitmapRectXY, paint);

Android - trouble in implementing this user interface

I am trying to implement a UI like this one..
http://www.shrenikvikam.com/wp-content/uploads/2011/04/214e422a43E11S3.png-150x134.png
But i am having some trouble implementing this.. Could someone tell me mistakes in this...
public class Meter extends View{
static final int ORBIT_COLOR = Color.argb(255, 66, 66, 66);
static final double RAD_CIRCLE = 2*Math.PI; // Number radians in a circle
private Paint paint; // Paint object controlling format of screen draws
private ShapeDrawable planet; // Planet symbol
private int planetRadius = 7; // Radius of spherical planet (pixels)
private int sunRadius = 12; // Radius of Sun (pixels)
private float X0 = 0; // X offset from center (pixels)
private float Y0 = 0; // Y offset from center (pixels)
private float X; // Current X position of planet (pixels)
private float Y; // Current Y position of planet (pixels)
private float centerX; // X for center of display (pixels)
private float centerY; // Y for center of display (pixels)
private float R0; // Radius of circular orbit (pixels)
private int nsteps = 120; // Number animation steps around circle
private double theta; // Angle around orbit (radians)
private double dTheta; // Angular increment each step (radians)
private double direction = -1; // Direction: counter-clockwise -1; clockwise +1
private float lastTouchX; // x coordinate of symbol i at last touch
private float lastTouchY; // x coordinate of symbol i at last touch
private int divisions = 120; // Since it requires temperature change from 0 -120
private double oneSegmentLength = (2 * Math.PI * R0)/(double)120;
public Meter(Context context) {
super(context);
// Initialize angle and angle step (in radians)
theta = 30;
//dTheta = RAD_CIRCLE/((double) nsteps); // Angle increment in radians
dTheta = ((360-60)/(double)divisions);
planet = new ShapeDrawable(new OvalShape());
planet.getPaint().setColor(Color.WHITE);
planet.setBounds(0, 0, 2*planetRadius, 2*planetRadius);
paint = new Paint();
paint.setAntiAlias(true);
paint.setTextSize(14);
paint.setStrokeWidth(1);
}
#Override
public boolean onTouchEvent(MotionEvent ev) {
int action = ev.getAction();
switch (action) {
// MotionEvent class constant signifying a finger-down event
case MotionEvent.ACTION_DOWN: {
final float x = ev.getX();
final float y = ev.getY();
lastTouchX = x;
lastTouchY = y;
newXY();
break;
}
// MotionEvent class constant signifying a finger-drag event
case MotionEvent.ACTION_MOVE: {
float newX = ev.getX();
float newY = ev.getY();
float dx = newX - lastTouchX;
float dy = newY - lastTouchY;
int diff = (int) (Math.abs(ev.getX()) % Math.abs(oneSegmentLength));
if(diff == 0){
if(Math.abs(dx) > Math.abs(dy)) {
if(dx>0) direction = 1;
else direction = -1;
newXY();
} else {
newXY();
}
Log.d("MOVE", "dx ->" + dx + " one seg->" + oneSegmentLength);
invalidate();
}
break;
}
// MotionEvent class constant signifying a finger-up event
case MotionEvent.ACTION_UP: {
Log.d("ACTION MOVE","Value ->");
final float x = ev.getX();
final float y = ev.getY();
lastTouchX = x;
lastTouchY = y;
newXY();
break;
}
}
return true;
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
drawBackground(paint, canvas);
canvas.save();
canvas.translate(X + X0, Y + Y0);
planet.draw(canvas);
canvas.restore();
}
// Called by onDraw to draw the background
private void drawBackground(Paint paint, Canvas canvas){
paint.setColor(Color.YELLOW);
paint.setStyle(Paint.Style.FILL);
canvas.drawCircle(centerX + X0, centerY + Y0, sunRadius, paint);
paint.setStyle(Paint.Style.STROKE);
paint.setColor(ORBIT_COLOR);
canvas.drawCircle(centerX + X0, centerY + Y0, R0, paint);
}
//Method to increment angle theta and compute the new X and Y .
private void newXY(){
theta += dTheta;
Log.d("THETA VAL", "->" + theta);
//if(theta > RAD_CIRCLE) theta -= RAD_CIRCLE; // For convenience, keep angle 0-2pi
if(theta > 150)theta = 30;
if(theta > 30 && theta <120){
X = (float)(R0*Math.sin(direction*theta)) + centerX - planetRadius;
Y = centerY - (float)(R0*Math.cos(direction*theta)) - planetRadius;
}
//Log.i("ANIMATOR", "X="+X+" Y="+Y);
}
#Override
protected void onSizeChanged (int w, int h, int oldw, int oldh){
// Coordinates for center of screen
centerX = w/2;
centerY = h/2;
// Make orbital radius a fraction of minimum of width and height of display
R0 = (float) (0.90*Math.min(centerX, centerY));
oneSegmentLength = (2 * Math.PI * R0)/(double)120;
// Set the initial position of the planet (translate by planetRadius so center of planet
// is at this position)
X = centerX - planetRadius ;
Y = centerY - R0 - planetRadius;
}
}
I am referring this code to do this implementation...
http://eagle.phys.utk.edu/guidry/android/animatorDemo.html
I am just drawing a circle and trying to implement the same motion between 0 -120 degrees..

Resources