Создать шестиугольное поле с JavaFX
У меня есть цель создать поле из шестиугольных плиток. Я дошел до того, что у меня есть матрица ячеек, каждая из которых достаточно высока, чтобы соответствовать полному шестиугольному изображению:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
public class UITest extends Application {
final private static String TILE_IMAGE_LOCATION = System.getProperty("user.dir") + File.separatorChar +"resources"+ File.separatorChar + "blueTile.png";
final private static Image HEXAGON_IMAGE = initTileImage();
private static Image initTileImage() {
try {
return new Image(new FileInputStream(new File(TILE_IMAGE_LOCATION)));
} catch (FileNotFoundException e) {
throw new IllegalStateException(e);
}
}
public void start(Stage primaryStage) {
int height = 4;
int width = 6;
GridPane tileMap = new GridPane();
Scene content = new Scene(tileMap, 800, 600);
primaryStage.setScene(content);
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
ImageView tile = new ImageView(HEXAGON_IMAGE);
GridPane.setConstraints(tile, x, y);
tileMap.getChildren().add(tile);
}
}
primaryStage.show();
}
}
Моя проблема не в вертикальном зазоре, который я наверняка могу выяснить, добавив GridPane's vGap()
к правильной стоимости. Сложность для меня заключается в смещении каждого второго ряда на половину ширины ячейки вправо.
Я попытался положить две GridPanes поверх друг друга, одна из которых содержала нечетные, а другая четные строки, с целью добавить отступы к одной из них, полностью сдвинув ее. Однако, насколько мне известно, для этого нет способа, как и вложение GridPanes в другое.
Как лучше всего добиться сдвига только в каждом втором ряду?
(Изображение, на которое я ссылаюсь в коде, который ожидается в ${projectroot}/resources/
папка: )
2 ответа
Мне потребовалось некоторое время, чтобы понять это. Я надеюсь, что это помогает. Я не использую изображение. Он сделан из полигонов, вы можете настроить обводку и цвет заливки, а также ширину.
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.scene.layout.AnchorPane;
import javafx.scene.paint.Paint;
import javafx.scene.shape.Polygon;
public class UITest extends Application {
public void start(Stage primaryStage) {
int height = 600;
int width = 800;
AnchorPane tileMap = new AnchorPane();
Scene content = new Scene(tileMap, width, height);
primaryStage.setScene(content);
double size = 50,v=Math.sqrt(3)/2.0;
for(double y=0;y<height;y+=size*Math.sqrt(3))
{
for(double x=-25,dy=y;x<width;x+=(3.0/2.0)*size)
{
Polygon tile = new Polygon();
tile.getPoints().addAll(new Double[]{
x,dy,
x+size,dy,
x+size*(3.0/2.0),dy+size*v,
x+size,dy+size*Math.sqrt(3),
x,dy+size*Math.sqrt(3),
x-(size/2.0),dy+size*v
});
tile.setFill(Paint.valueOf("#ffffff"));
tile.setStrokeWidth(2);
tile.setStroke(Paint.valueOf("#000000") );
tileMap.getChildren().add(tile);
dy = dy==y ? dy+size*v : y;
}
}
primaryStage.show();
}
public static void main(String[] args)
{
launch(args);
}
}
Для других заинтересованных душ я использовал принятый ответ Ктулху и улучшил / задокументировал данный код как короткую отдельную демонстрацию:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.AnchorPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Polygon;
import javafx.stage.Stage;
public class UISolution extends Application {
private final static int WINDOW_WIDTH = 800;
private final static int WINDOW_HEIGHT = 600;
private final static double r = 20; // the inner radius from hexagon center to outer corner
private final static double n = Math.sqrt(r * r * 0.75); // the inner radius from hexagon center to middle of the axis
private final static double TILE_HEIGHT = 2 * r;
private final static double TILE_WIDTH = 2 * n;
public static void main(String[] args) {
launch(args);
}
public void start(Stage primaryStage) {
AnchorPane tileMap = new AnchorPane();
Scene content = new Scene(tileMap, WINDOW_WIDTH, WINDOW_HEIGHT);
primaryStage.setScene(content);
int rowCount = 4; // how many rows of tiles should be created
int tilesPerRow = 6; // the amount of tiles that are contained in each row
int xStartOffset = 40; // offsets the entire field to the right
int yStartOffset = 40; // offsets the entire fiels downwards
for (int x = 0; x < tilesPerRow; x++) {
for (int y = 0; y < rowCount; y++) {
double xCoord = x * TILE_WIDTH + (y % 2) * n + xStartOffset;
double yCoord = y * TILE_HEIGHT * 0.75 + yStartOffset;
Polygon tile = new Tile(xCoord, yCoord);
tileMap.getChildren().add(tile);
}
}
primaryStage.show();
}
private class Tile extends Polygon {
Tile(double x, double y) {
// creates the polygon using the corner coordinates
getPoints().addAll(
x, y,
x, y + r,
x + n, y + r * 1.5,
x + TILE_WIDTH, y + r,
x + TILE_WIDTH, y,
x + n, y - r * 0.5
);
// set up the visuals and a click listener for the tile
setFill(Color.ANTIQUEWHITE);
setStrokeWidth(1);
setStroke(Color.BLACK);
setOnMouseClicked(e -> System.out.println("Clicked: " + this));
}
}
}