I'm newer to JavaFX and I'm attempting to make a checkerboard. I first want to start out by making a gridpane of squares that fill with random colors based on
an array. I'm not sure why, but the squares aren't filling the rest of the grid. I'd also like to use constraints to set the grid height and width.
int rowNum = 10;
int colNum = 10;
int gridHeight = 10;
int gridWidth = 10;
public void start(Stage primaryStage) {
GridPane grid = new GridPane();
//grid.getColumnConstraints().add(new ColumnConstraints(gridWidth));
//grid.getRowConstraints().add(new RowConstraints(gridHeight));
Random rand = new Random();
Color[] colors = {Color.BLACK, Color.BLUE, Color.GREEN, Color.RED};
int n = rand.nextInt(4)+1;
for(int row = 0; row < rowNum; row++){
for(int col = 0; col < colNum; col++){
Rectangle rec = new Rectangle();
rec.setWidth(2);
rec.setHeight(2);
rec.setFill(colors[n]);
GridPane.setRowIndex(rec, row);
GridPane.setColumnIndex(rec, col);
grid.getChildren().addAll(rec);
}
}
Scene scene = new Scene(grid, 350, 250);
primaryStage.setTitle("Grid");
primaryStage.setScene(scene);
primaryStage.show();
}
Only one square appears on the top left.
Why is this the case?
You need to move the random number generation step inside the two loops so that you're not using the same color that has been set once before entering the loop. Also, you don't need the plus 1 on that random number generator. The allowed indices are 0-3. When you ask for the nextInt() and input a 4, that's exclusive which means 4 will never be picked (which is what you need for the Color array).
Your code should look like this:
for (int row = 0; row < rowNum; row++) {
for (int col = 0; col < colNum; col++) {
int n = rand.nextInt(4);
Rectangle rec = new Rectangle();
rec.setWidth(2);
rec.setHeight(2);
rec.setFill(colors[n]);
GridPane.setRowIndex(rec, row);
GridPane.setColumnIndex(rec, col);
grid.getChildren().addAll(rec);
}
}
Related
I want to make random rectangles on Processing. So far, I used for loops for making window size rectangle but I can't figure out how to make only 10 rectangle randomly. Here is my sample code for you:
void setup()
{
size(400, 400);
}
void draw()
{
background(0); // Black Background
stroke(255); // White lines
for (int j = 0; j <= height; j += 40)
{
for (int i = 0; i < width; i += 40)
{
fill(0);
rect(i, j, 40, 40);
}
}
}
It shows 100 black rectangles but I want to see only 10 black rectangles. For example: The first line will get random 1 rectangle, second line will get 2 , third line will get 1 and it goes till the 10.
There are multiple ways to solve this fun homework/exercise.
First thing is drawing the right number of boxes per column:
void setup()
{
size(400, 400);
background(0); // Black Background
fill(0);
stroke(255); // White lines
int boxSize = 40;
int maxBoxes = 1;
for (int j = 0; j <= height; j += boxSize)
{
// box count per row
int boxCount = 0;
for (int i = 0; i < width; i += boxSize)
{
// only draw the max number of boxes
if(boxCount < maxBoxes){
rect(i, j, 40, 40);
// increment per row box count
boxCount++;
}
}
// increment max boxes per box
maxBoxes++;
}
}
Secondly the positions for the drawn boxes per column need to be randomized, but ideally not overlap. One option is to split the full solution space to sections: each section having it's own range of positions so it won't overlap the next.
void setup()
{
size(400, 400);
background(0); // Black Background
fill(0);
stroke(255); // White lines
int boxSize = 40;
int maxBoxes = 1;
int totalBoxes = width / boxSize;
for (int j = 0; j <= height; j += boxSize)
{
// box count per row
int boxCount = 0;
// a list of box indices of where to draw a box (as opposed
int[] randomXIndices = new int[maxBoxes];
// how many index ranges to span per row
int indexRangePerBox = totalBoxes / maxBoxes;
// for each random index
for(int k = 0 ; k < maxBoxes; k++)
{
// pre-calculate which random index to select
// using separate ranges per box to avoid overlaps
randomXIndices[k] = (int)random(indexRangePerBox * k, indexRangePerBox * (k + 1));
}
for (int i = 0; i < width; i += boxSize)
{
// only draw the max number of boxes
if(boxCount < maxBoxes)
{
int randomX = randomXIndices[boxCount] * boxSize;
rect(randomX, j, 40, 40);
// increment per row box count
boxCount++;
}
}
// increment max boxes per box
maxBoxes++;
}
}
void draw(){
}
void mousePressed(){
setup();
}
Click to reset. Notice that the bottom rows almost always look the same:
there is less wiggle room to pick a random position
random() is a rough pseudo-random number generator, but there are better ones out there like randomGaussian(), noise(), etc.
overall there are other strategies to explore picking random positions and avoiding overlaps
Live demo bellow:
function setup()
{
createCanvas(400, 400);
reset();
// reset once per second
setInterval(reset, 1000);
}
function reset(){
background(0); // Black Background
fill(0);
stroke(255); // White lines
var boxSize = 40;
var maxBoxes = 1;
var totalBoxes = width / boxSize;
for (var j = 0; j <= height; j += boxSize)
{
// box count per row
var boxCount = 0;
// a list of box indices of where to draw a box (as opposed
var randomXIndices = new Array(maxBoxes);
// how many index ranges to span per row
var indexRangePerBox = totalBoxes / maxBoxes;
// for each random index
for(var k = 0 ; k < maxBoxes; k++)
{
// pre-calculate which random index to select
// using separate ranges per box to avoid overlaps
randomXIndices[k] = floor(random(indexRangePerBox * k, indexRangePerBox * (k + 1)));
}
for (var i = 0; i < width; i += boxSize)
{
// only draw the max number of boxes
if(boxCount < maxBoxes)
{
var randomX = randomXIndices[boxCount] * boxSize;
rect(randomX, j, 40, 40);
// increment per row box count
boxCount++;
}
}
// increment max boxes per box
maxBoxes++;
}
}
function draw(){
}
function mousePressed(){
reset();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.0.0/p5.min.js"></script>
I am creating a "threes" clone for my assignment and one of the properties was to have a dynamically sizable GridPane depending on the number of columns and rows, how can I achieve this?
I've looked at adding ColumnConstraints as well as RowConstraints, given them a percentile width of the total GridPane size divided by the number of columns:
// width = total / number of columns
// height = total / number of rows
In Scene Builder the GridPane has by default a 4x4 size; these elements seem to resize just fine but when say a 4x5 size is loaded it seems to only want to show 4x4 (styled) and the other remaining tiles look "weird".
Here's what it looks like: https://imgur.com/gallery/Qv1oWZb
// width = total / number of columns
// height = total / number of rows
gridPane.setGridLinesVisible(false);
gridPane.setAlignment(Pos.TOP_LEFT);
gridPane.getChildren().clear();
RowConstraints rowConstraint = new RowConstraints();
rowConstraint.setPercentHeight(300 / game.getBoardSizeY());
ColumnConstraints colConstraint = new ColumnConstraints();
colConstraint.setPercentWidth(350 / game.getBoardSizeX());
for(int x= 0; x < game.getBoardSizeX(); x++) {
gridPane.getColumnConstraints().add(colConstraint);
for(int y = 0; y < game.getBoardSizeY(); y++) {
gridPane.getRowConstraints().add(rowConstraint);
Tile tile = game.getBoard().getPos(x, y);
Pane pane = new Pane();
String lblText = "";
String itemClass = "";
if(tile.getValue() != 0) {
lblText = String.valueOf(tile.getValue());
}
int tileVal = tile.getValue();
if(tileVal == 1) {
itemClass = "blueTile";
}else if (tileVal == 2) {
itemClass = "redTile";
}else if (tileVal >= 3 ) {
itemClass = "whiteTile";
}else {
itemClass = "defaultTile";
}
Label lblVal = new Label(lblText);
pane.getStyleClass().addAll("tile", itemClass);
lblVal.layoutXProperty().bind(pane.widthProperty().subtract(lblVal.widthProperty()).divide(2));
pane.getChildren().add(lblVal);
gridPane.add(pane, x, y);
}
}
I expect it to fill my entire GridPane accordingly but instead it acts up and shows me the result shown in the images.
Edit:
I've gotten it to work however when I have differentiating rows (like 4x5 for example) it doesn't quite work out the sizing yet.
gridPane.setGridLinesVisible(false);
gridPane.setAlignment(Pos.TOP_LEFT);
gridPane.getChildren().clear();
gridPane.getChildren().removeAll();
gridPane.getColumnConstraints().clear();
gridPane.getRowConstraints().clear();
RowConstraints rowConstraint = new RowConstraints();
rowConstraint.setPercentHeight(350 / game.getBoardSizeY());
ColumnConstraints colConstraint = new ColumnConstraints();
colConstraint.setPercentWidth(300 / game.getBoardSizeX());
for(int x= 0; x < game.getBoardSizeX(); x++) {
gridPane.getColumnConstraints().add(colConstraint);
gridPane.getRowConstraints().add(rowConstraint);
for(int y = 0; y < game.getBoardSizeY(); y++) {
Tile tile = game.getBoard().getPos(x, y);
Pane pane = new Pane();
String lblText = "";
String itemClass = "";
if(tile.getValue() != 0) {
lblText = String.valueOf(tile.getValue());
}
int tileVal = tile.getValue();
if(tileVal == 1) {
itemClass = "blueTile";
}else if (tileVal == 2) {
itemClass = "redTile";
}else if (tileVal >= 3 ) {
itemClass = "whiteTile";
}else {
itemClass = "defaultTile";
}
Label lblVal = new Label(lblText);
pane.getStyleClass().addAll("tile", itemClass);
lblVal.layoutXProperty().bind(pane.widthProperty().subtract(lblVal.widthProperty()).divide(2));
pane.getChildren().add(lblVal);
gridPane.add(pane, x, y);
}
}
If I understood correctly, you want the rows and columns to resize so that all columns have the same width and all rows have the same height. If that's correct, this sounds wrong:
I've looked at adding columnConstraints as well as rowConstraints, given them a percentile width of the total gridpane size devided by the number of columns
// width = total / number of columns
// height = total / number of rows
The percentile width/height is a relative measurement. You don't wan't to calculate that based on the current width/height. For instance, if you had 4 rows you'd have each row be 25% of the height.
That said, I don't believe you need to mess around with the width or height settings on the constraints. Just set the RowConstraints.vgrow and ColumnConstraints.hgrow properties to Priority.ALWAYS. Here's an example:
import javafx.application.Application;
import javafx.geometry.HPos;
import javafx.geometry.Pos;
import javafx.geometry.VPos;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.ColumnConstraints;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.RowConstraints;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
GridPane root = new GridPane();
root.setAlignment(Pos.CENTER);
root.setGridLinesVisible(true);
for (int col = 0; col < 5; col++) {
root.getColumnConstraints()
.add(new ColumnConstraints(-1, -1, -1, Priority.ALWAYS, HPos.CENTER, false));
for (int row = 0; row < 3; row++) {
if (col == 0) {
root.getRowConstraints()
.add(new RowConstraints(-1, -1, -1, Priority.ALWAYS, VPos.CENTER, false));
}
root.add(new Label(String.format("(%d,%d)", row, col)), col, row);
}
}
primaryStage.setScene(new Scene(root, 500, 300));
primaryStage.show();
}
}
I'm having trouble editing this code on having it mirror an image from left to right because when I received the code it mirrored the image on right to left.
public void mirrorVerticalRightToLeft() {
Pixel[][] pixels = this.getPixels2D();
Pixel leftPixel = null;
Pixel rightPixel = null;
int width = pixels[0].length;
for (int row = 0; row < pixels.length; row++) {
for (int col = 0; col > width/2 ; col++) {
leftPixel = pixels[row][col];
rightPixel = pixels[row][col-1-width];
rightPixel.setColor(leftPixel.getColor());
}
}
}
I think that the inner for loop does not run at all, as the condition is wrong.
It should be "col < width/2", instead of "col > width/2".
At the beginning col = 0, so it's not really larger than width/2, and loop exits immediately, without doing any work.
What I need to do:
I trying to write some code for visualise every pixel of any character. I decided best way for it will be show pixels as rectangle in Tile Pane. So this is effect which i need to reach:
What's my problem:
I wrote some code to make it by take snaphost of text1 and save it as WritableImage. Then i use PixelReader for read each pixel of this image by argb method. In loop when each integer rgb value is greater than TRESHOLD (is brighter) I add new tile with white background. Unless I add tile with black background. But something is still wrong with my idea and I get this effect:
There is my code:
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception{
//Create single letter string
String letter = "a";
//Create new text
Text text1 = new Text(letter);
//Set font for text
text1.setFont(Font.font("Calibri", FontWeight.NORMAL, 12));
//Save text1 as writable image
WritableImage newImg = text1.snapshot(null, null);
//Take bounds of letter
int imgHeight = (int) newImg.getHeight();
int imgWidth = (int) newImg.getWidth();
//Create pixel reader from newImg
PixelReader reader = newImg.getPixelReader();
//This is for preview image
ImageView imgPreview = new ImageView(newImg);
//Create tilePane
TilePane tilePane = new TilePane();
//New group with tilePane inside
Group display = new Group(tilePane);
//Bg color
tilePane.setStyle("-fx-background-color: gray;");
//Small gaps between tiles
tilePane.setHgap(2);
tilePane.setVgap(2);
//Set quantity of columns equals image width
tilePane.setPrefColumns(imgWidth);
//Set quantity of rows equals image height
tilePane.setPrefRows(imgHeight );
//Here I set tolerance treshold
int TOLERANCE_THRESHOLD = 0xf0;
for (int x = 0; x < imgWidth; x++) {
for (int y = 0; y < imgHeight; y++) {
int argb = reader.getArgb(x, y);
int r = (argb >> 16) & 0xFF;
int g = (argb >> 8) & 0xFF;
int b = argb & 0xFF;
if (r >= TOLERANCE_THRESHOLD
&& g >= TOLERANCE_THRESHOLD
&& b >= TOLERANCE_THRESHOLD) {
tilePane.getChildren().add(createElement(Color.WHITE));
}
else tilePane.getChildren().add(createElement(Color.BLACK));
}
}
// Create new stage
Stage stage = new Stage();
//Create new group
Group grupa = new Group();
Scene scene = new Scene(grupa, 400, 300, Color.GRAY);
grupa.getChildren().addAll(display);
stage.setScene(scene);
stage.show();
}
private Rectangle createElement(Color color) {
Rectangle rectangle = new Rectangle(15, 15);
//rectangle.setStroke(Color.ORANGE);
rectangle.setFill(color);
return rectangle;
}
public static void main(String[] args) {
launch(args);
}
}
What I doing wrong? Maby are other ways to take this effect?
Your outer loop variable is the x-coordinate and the inner loop variable is the y-coordinate.
This means you read the pixels column by column left to right. TilePane however requires the children list to be ordered row by row from top to bottom.
To fix the issue simply swap the loops. Use:
for (int y = 0; y < imgHeight; y++) {
for (int x = 0; x < imgWidth; x++) {
...
}
}
Instead of
for (int x = 0; x < imgWidth; x++) {
for (int y = 0; y < imgHeight; y++) {
...
}
}
I've been doing a CrossWord puzzle in java for a project. However I haven't been able to fix one problem I'm having with drawing the CrossWord into a JPanel
The program takes the data from a 2D array of chars and generates a grid of a custom type of JLabels with it. This is the code:
public void update(Observable o, Object o1) {
if(model.getMatrix() != null){
configurePanel(model.getRows(), model.getCols());
this.add(panel);
this.pack();
this.revalidate();
this.repaint();
}
}
private void configurePanel(int w, int h) {
if (panel!=null){
this.remove(panel);
}
panel = new JPanel();
panel.setBounds(40, 40, w*50, h*50);
panel.setLayout(new GridLayout(w, h));
labels = createLabels(model.getMatrix());
for (int i = 0; i < w; i++){
for(int j = 0; j < h; j++){
panel.add(labels[i][j]);
}
}
}
private CWlabel[][] createLabels(char[][] matrix) {
int w = matrix.length;
int h = matrix[0].length;
labels = new CWlabel[w][h];
for (int i = 0; i < w; i++){
for(int j = 0; j < h; j++){
char c = matrix[i][j];
labels[i][j] = new CWlabel();
labels[i][j].setBorder(BorderFactory.createLineBorder(Color.black));
labels[i][j].setOpaque(true);
if (c != ' '){
labels[i][j].setBackground(Color.white);
} else {
labels[i][j].setBackground(Color.black);
}
}
}
return labels;
}
My main problem is in configurePanel(), where the size of the panel are set proportional to the crossword dimensions, which should ensure every component inside it is perfectly square, however this is not the case as seen here
In the example shown the difference is subtle but still notizable. The strange part its that if I manually replace,
panel.setBounds(40, 40, w*50, h*50);
with,
panel.setBounds(40, 40, 400, 450);
the result appears to be proportional to the amount of rows and columns as shown here
By my count you have 9 rows and 8 columns so calling configurePanel like the following:
configurePanel(model.getRows(), model.getCols());
Is going to result in a width of 450 and a height of 400, but you want a width of 400 and a height of 450. Thus switch around the first and second parameters, like the following:
configurePanel(model.getCols(), model.getRows());
Note: You shouldn't use absolute positioning, it causes many formatting problems down the road (resizing components, adding components, etc.). You should instead use a layout manager with more customization like GridBagLayout or MigLayout.