How to get screen/scene center x,y of Rectangle within a GridPane? - javafx-8

I'm working on a simple board game implementation in JavaFX8.
For the game board, my decision was to use a 10x10 GridPane and fill it's cells with Rectangles in form's initialize method.
private void drawBoard() {
gridpaneBoard.getChildren().clear();
for (int y = 0; y < gridpaneBoard.getRowConstraints().size(); y++)
for (int x = 0; x < gridpaneBoard.getColumnConstraints().size(); x++) {
Rectangle rect = new Rectangle(55,55);
rect.setStroke(Color.BLACK);
Tile tile = GameController.getInstance().getBoard().getTile(x, y);
if (tile.hasBranch())
rect.setFill(QuestionDifficulty.values()[tile.getBranch()
.getQuestion().getQuestion()
.getLevel()].getColor());
else
rect.setFill(Color.WHITE);
gridpaneBoard.add(rect, x, y);
gridpaneBoard.add(new Label(String.valueOf(tile.getNumber())), x, y);
}
}
In order to animate player token movement after dice rolls, I figured I need to know the center x & center y of each tile (to create a path transition from source tile to destination tile).
I've tried all sorts of answers given to other people's questions, but everything returned 0,0 for me.
This is the container hierarchy in this scene:
This is how the output looks at the moment:
If GridPane is fine for what I'm trying to achieve, how can I get a child's (in this case a rectangle's) screen / scene center x,y?
If GridPane is not fine, can you point me to alternatives and how I can achieve what I want then..
Thank you!

You can simply call getBoundsInParent to get the dimensions of the Node in it's parent.
The following example is a bit simplified, but it should demonstrate the approach nonetheless:
#Override
public void start(Stage primaryStage) {
GridPane gridpaneBoard = new GridPane();
for (int y = 0; y < 10; y++) {
for (int x = 0; x < 10; x++) {
Rectangle rect = new Rectangle(55, 55);
rect.setStroke(Color.BLACK);
rect.setFill((x + y) % 2 == 0 ? Color.WHITE : Color.DARKGRAY);
gridpaneBoard.add(rect, x, y);
}
}
gridpaneBoard.setOnMouseClicked(evt -> {
Node target = evt.getPickResult().getIntersectedNode();
if (target != gridpaneBoard) {
// in your case you'd need to make sure this is not the Label
Bounds bounds = target.getBoundsInParent();
System.out.println("bounds = " +bounds);
System.out.println("centerX = " +(bounds.getMinX() + bounds.getWidth()/2));
System.out.println("centerY = " +(bounds.getMinY() + bounds.getHeight()/2));
}
});
Scene scene = new Scene(gridpaneBoard);
primaryStage.setScene(scene);
primaryStage.show();
}
If coordinates different to the GridPane coordinates are required, you could use getBoundsInLocal in combination with localTo... instead:
Bounds bounds = target.localToScene(target.getBoundsInLocal());
for scene bounds or
Bounds bounds = target.localToScreen(target.getBoundsInLocal());
for screen bounds.
Note: This works independent from any properties modifying how GridPane layouts it's children.

You can use :
-GridPane.getColumnIndex(Node) to get the column index.
-GridPane.getRowIndex(Node) to get the row index.
-Since you know the Width and the Height (55,55) of your child (Rectangle) you can just calculate its centerX, centerY which is relative to its position in the container, but since you use a GridPane I don't think it is possible since this one has Constraints. You can fix it by changing the container of your object or completely redraw it inside another raw/column, here is an example Replace a node at (row,col).

Related

Unity: Dynamically build UI elements on Canvas: ScreenSpace-Camera, previously ScreenSpace-Overlay

My application works fine, 100% expected results with Canvas set on ScreenSpace-Overlay. I have written a function that takes an array of integers and based on its values it dynamically builds vertical bars inside a panel. I have an UI-Image as a prefab and that I instantiated multiple time, set its anchor to bottom left position and increase the x-offset from that point in increments of its width+padding. Everything works fine, check the screenshots.
I have now a fancy idea of animating the vertical bars... to have Unity Particle Effects on the top of each bar and to play it. From what I searched... you cannot get Particle Effects working on Canvas-UI unless the Canvas is set as ScreenSpace-Camera. I managed to obtain success with Particle Effects and ScreenSpace-Camera canvas... everything works... but now the same code that dynamically builds the UI doesnt work anymore... the vertical bars are set on a total new position outside the UI.
My Canvas
Working algorithm
My code:
private void ArrangeImagesOnCanvas(List<int> varArray)
{
Canvas canvas = FindObjectOfType<Canvas>();
//float h = canvas.GetComponent<RectTransform>().rect.height * canvas.GetComponent<RectTransform>().localScale.y;
//float w = canvas.GetComponent<RectTransform>().rect.width * canvas.GetComponent<RectTransform>().localScale.x;
float h = _panelImageForSortingAlgorithm.GetComponent<RectTransform>().rect.height * canvas.GetComponent<RectTransform>().localScale.y;
float w = _panelImageForSortingAlgorithm.GetComponent<RectTransform>().rect.width * canvas.GetComponent<RectTransform>().localScale.x;
float minH = 20.0f;
float maxH = h - 20;
int minList = varArray.Min();
int maxList = varArray.Max();
_imageWidth = (w - (varArray.Count * _PADDING)) / varArray.Count;
List<float> heightsList = new List<float>();
float _tempFloat = 0;
for (int i = 0; i < varArray.Count; i++)
{
_tempFloat = ScaleIntervals(varArray[i], new Vector2(minList, maxList), new Vector2(minH, maxH));
heightsList.Add(_tempFloat);
}
for(int i = 0; i<_instancesOfImages.Count;i++)
{
Destroy(_instancesOfImages[i]);
}
_instancesOfImages.Clear();
for (int i = 0; i < varArray.Count; i++)
{
GameObject instantiatedImage = Instantiate(_imageToInstance, _imageToInstance.transform.position, Quaternion.identity);
instantiatedImage.transform.SetParent(_panelImageForSortingAlgorithm.transform);
Image img = instantiatedImage.GetComponent<Image>();
img.rectTransform.anchorMin = new Vector2(0, 0);
img.rectTransform.anchorMax = new Vector2(0, 0);
img.rectTransform.pivot = new Vector2(0, 0);
img.rectTransform.position = new Vector3(i * (_imageWidth + _PADDING), 0, 0);
img.rectTransform.sizeDelta = new Vector2(_imageWidth, heightsList[i]);
_instancesOfImages.Add(instantiatedImage);
}
}
For the same code the vertical bars can be seen in this image...
Broken bars 1
if I put
float h = _panelImageForSortingAlgorithm.GetComponent<RectTransform>().rect.height;
float w = _panelImageForSortingAlgorithm.GetComponent<RectTransform>().rect.width;
then the vertical bars are like in this image.
Broken bars 2
How can I position correctly the vertical bars as in ScreenSpace-Overlay but this time to be on ScreenSpace-Camera ? Please help or give me some advices or tips...

how to copy a part of a raw image

I want to copy selected part of a raw image to another image
I get start and end position as percentage and by that I can calculate the start and end position in width
how can I copy that selected part to another raw image?
Assuming it's a Texture2D, you can do the following:
Calculate A texture start/end X (dX)
Create a new Texture2D (B), sized as dX and full Y
Call A.GetPixels()
Iterate on array copying pixels to new texture
Apply on new texture
Pseudo code:
var aPixels = aTexture.GetPixels();
var bWidth = endX - startX;
var bTexture = new Texture2D(bWidth, endY);
var bPixels = bTexture.GetPixels();
for (int x = startX; x < endX; x++)
{
for (int y = 0; y < endY; y++)
{
var aIndex = x + y * A.width;
var bIndex = (x - startX) + y * bWidth;
bPixels[bIndex] = aPixels[aIndex];
}
}
bTexture.Apply();
Note that my code quite possibly won't work; as I'm typing this on a mobile phone.
Usually, Image Processing is an expensive process for CPUs, so I don't recommend it in Unity,
But anyway, For your image and in this special case, I think you can crop your image by changing the Size and Offset of texture in material.
Update:
This is an example of what I mentioned:
You can calculate Tile and Offset based on the dragged mouse position on Texture. (Check Here)
I found this.
you can set start coordinates and width and height to GetPixels();
void Start () {
public Texture2D mTexture;
Color[] c = mTexture.GetPixels (startX, startY, width, height);
Texture2D m2Texture = new Texture2D (width, height);
m2Texture.SetPixels (c);
m2Texture.Apply ();
gameObject.GetComponent<MeshRenderer> ().material.mainTexture = m2Texture;
}
```

Unity: Make a tile take up more grid cells

I am trying to create a basic tower defense game where I have set up my cell size to be 0.5 X and 0.5 Y (so that the player can place their towers more freely, think WC3).
This causes problems when I later in the game wants to check if a grid cell is occupied, because some cells will seem to be taken, but actually are not.
Here's an image to illustrate my problem:
The black square is rendered over 4 cells, but only 1 of the cells are occupied (the white square in the lower left corner of the black square).
Have anyone else faced this particular problem and knows how to solve this or are there any other solutions that you would like to recommend? :)
Thanks in advance!
Though I'm unsure how the unity Grid component works I have used the following approach in one of my grid based games. Note however that I implemented my own grid for this, which contain custom grid tiles. But maybe the same logic can be applied to the unity grid
This grid was just a simple grid made like this
for (int i = 0; i < terrainLength; i++)
{
for (int j = 0; j < terrainWidth; j++)
{
GameObject tile = Instantiate(gridTilePrefab, new Vector3(posX + i * gridCube.transform.localScale.x, posY + terrainHeight, posZ + j * gridCube.transform.localScale.z), Quaternion.identity);
tile.name = "grid[" + i + "," + j + "]";
tile.transform.parent = gridParent.transform;
}
}
These grid tiles would have a boolean isOccupied. That would be set to true if an object is placed on it, and false if not.
To check wether or not it was occupied I would simply cast a raycast up from the center of the tile and check for any collision while in the builder phase (No need to do these checks during play!) the implemented was a simple as this:
Class GridTile
{
public bool isOccupied {get; private set;}
public void BuildStageLoop()//this loops like an update while we're in building stage
{
if (Physics.Raycast(transform.position, Vector3.up * 2, out hit))
{
tileOccupied = true;
}
else
{
tileOccupied = false;
}
}
}
And on the placing object I would just check if every tile underneath it had isOccupied set to false. To check for the tiles underneath it I would do a boxRayCast downwards with the width and length of the object you're trying to place, and extending a bit underneath the object so it can collide with the grid tiles.

EaselJS shape x,y properties confusion

I generate a 4x4 grid of squares with below code. They all draw in correct position, rows and columns, on canvas on stage.update(). But the x,y coordinates for all sixteen of them on inspection are 0,0. Why? Does each shape has it's own x,y coordinate system? If so, if I get a handle to a shape, how do I determine where it was drawn originally onto the canvas?
The EaselJS documentation is silent on the topic ;-). Maybe you had to know Flash.
var stage = new createjs.Stage("demoCanvas");
for (i = 0; i < 4; i++) {
for (j = 0; j < 4; j++) {
var square = new createjs.Shape();
square.graphics.drawRect(i*100, j*100, 100, 100);
console.log("Created square + square.x + "," + square.y);
stage.addChild(square);
}
}
You are drawing the graphics at the coordinates you want, instead of drawing them at 0,0, and moving them using x/y coordinates. If you don't set the x/y yourself, it will be 0. EaselJS does not infer the x/y or width/height based on the graphics content (more info).
Here is an updated fiddle where the graphics are all drawn at [0,0], and then positioned using x/y instead: http://jsfiddle.net/0o63ty96/
Relevant code:
square.graphics.beginStroke("red").drawRect(0,0,100,100);
square.x = i * 100;
square.y = j * 100;

JavaFX 8, how to get center location of Scrollpane's Viewport

The Problem
I'm trying to figure out a way to get at which point in the content node the scroll pane's viewport is centered on.
To elaborate on the picture above, the big rectangle is the content (let's say a large image), and the small rectangle is the portion that is shown by the scroll pane. I'm trying to find x and y which would be coordinates from the top left of the content.
What I've Tried
My first thought was to use the getViewportBounds() method of the scroll pane and use its minX and maxX properties to determine the center x point:
Bounds b = scrollPane.getViewportBounds();
double centerX = (b.getMinX() + b.getMaxX()) / 2;
double centerY = (b.getMinY() + b.getMaxY()) / 2;
However, this doesn't work because these numbers are negative and don't seem to accurately describe the x and y I'm looking for anyways.
My next thought was to use the scroll pane's hValue and vValue to get the top left corner of the viewport relative to the content:
Bounds b = scrollPane.getViewportBounds();
double centerX = scrollPane.getHvalue() + b.getWidth() / 2;
double centerY = scrollPane.getVvalue() + b.getHeight() / 2;
This didn't work either though as the hValue and vValue seem to be way too large (when scrolled in only a few pixels, I'm getting numbers like 1600).
My Questions
I seem to have a fundamental misunderstanding of how the viewport works with a scroll pane.
What am I doing wrong here? Can someone explain where these numbers come from? How do I find x and y like in the picture above?
Let (x, y) be the be coordinates of the top, left point shown in the viewport. You can write this as
((contentWidth - viewportWidth) * hValueRel, (contentHeight - viewportHeight) * vValueRel)
vValueRel = vValue / vMax
hValueRel = hValue / hMax
This means assuming hmin and vmin remain 0 you can keep a circle in the center of like this:
// update circle position to be centered in the viewport
private void update() {
Bounds viewportBounds = scrollPane.getViewportBounds();
Bounds contentBounds = content.getBoundsInLocal();
double hRel = scrollPane.getHvalue() / scrollPane.getHmax();
double vRel = scrollPane.getVvalue() / scrollPane.getVmax();
double x = Math.max(0, (contentBounds.getWidth() - viewportBounds.getWidth()) * hRel) + viewportBounds.getWidth() / 2;
double y = Math.max(0, (contentBounds.getHeight() - viewportBounds.getHeight()) * vRel) + viewportBounds.getHeight() / 2;
Point2D localCoordinates = content.parentToLocal(x, y);
circle.setCenterX(localCoordinates.getX());
circle.setCenterY(localCoordinates.getY());
}
private Circle circle;
private Pane content;
private ScrollPane scrollPane;
#Override
public void start(Stage primaryStage) {
// create ui
circle = new Circle(10);
content = new Pane(circle);
content.setPrefSize(4000, 4000);
scrollPane = new ScrollPane(content);
Scene scene = new Scene(scrollPane, 400, 400);
// add listener to properties that may change
InvalidationListener l = o -> update();
content.layoutBoundsProperty().addListener(l);
scrollPane.viewportBoundsProperty().addListener(l);
scrollPane.hvalueProperty().addListener(l);
scrollPane.vvalueProperty().addListener(l);
scrollPane.hmaxProperty().addListener(l);
scrollPane.vmaxProperty().addListener(l);
scrollPane.hminProperty().addListener(l);
scrollPane.vminProperty().addListener(l);
primaryStage.setScene(scene);
primaryStage.show();
}