package org.nusco.narjillos.application.views; import javafx.scene.Group; import javafx.scene.Node; import javafx.scene.Scene; import javafx.scene.image.WritableImage; import javafx.scene.paint.Color; import javafx.scene.paint.ImagePattern; import javafx.scene.shape.Circle; import javafx.scene.shape.Rectangle; import javafx.scene.shape.Shape; import javafx.scene.transform.Scale; import javafx.scene.transform.Translate; import org.nusco.narjillos.application.utilities.Viewport; /** * The speckles in the petri dish's liquid. They provide a sense of a tracked * narjillo's movement, speed and direction. */ class SpecklesView { private static final double NORMAL_SPECKLE_RADIUS = 2; private static final double INFRARED_SPECKLE_RADIUS = 2.5; private static final Color SPECKLE_COLOR = EnvironmentView.BACKGROUND_COLOR.darker(); private final Viewport viewport; private final Shape background; private final Shape infraredBackground; private WritableImage backgroundTexture; private WritableImage infraredBackgroundTexture; public SpecklesView(Viewport viewport, long ecosystemSize) { this.viewport = viewport; createBackgroundTextures(); background = new Rectangle(0, 0, ecosystemSize, ecosystemSize); background.setFill(new ImagePattern(backgroundTexture, 0, 0, viewport.getSizeSC().x, viewport.getSizeSC().y, false)); infraredBackground = new Rectangle(0, 0, ecosystemSize, ecosystemSize); infraredBackground .setFill(new ImagePattern(infraredBackgroundTexture, 0, 0, viewport.getSizeSC().x, viewport.getSizeSC().y, false)); } public Node toNode(boolean infraredOn) { Node result = infraredOn ? infraredBackground : background; double minimumZoomLevelToSeeSpeckles = 0.20; if (viewport.getZoomLevel() <= minimumZoomLevelToSeeSpeckles) return null; result.getTransforms().clear(); result.getTransforms().add(new Translate(-viewport.getPositionEC().x, -viewport.getPositionEC().y)); result.getTransforms().add( new Scale(viewport.getZoomLevel(), viewport.getZoomLevel(), viewport.getPositionEC().x, viewport.getPositionEC().y)); return result; } private void createBackgroundTextures() { final int tileSize = 600; // TODO: remove this duplication Group backgroundGroup = new Group(); Scene offScreenBackgroundScene = new Scene(backgroundGroup, tileSize, tileSize); Shape emptySpace = new Rectangle(0, 0, tileSize, tileSize); emptySpace.setFill(EnvironmentView.BACKGROUND_COLOR); backgroundGroup.getChildren().add(emptySpace); Group infraredBackgroundGroup = new Group(); Scene offScreenInfraredBackgroundScene = new Scene(infraredBackgroundGroup, tileSize, tileSize); Shape infraredEmptySpace = new Rectangle(0, 0, tileSize, tileSize); infraredEmptySpace.setFill(EnvironmentView.INFRARED_BACKGROUND_COLOR); infraredBackgroundGroup.getChildren().add(infraredEmptySpace); for (int i = 0; i < 5; i++) { int x = getRandomCoordinate(tileSize); int y = getRandomCoordinate(tileSize); backgroundGroup.getChildren().add(createSpeckle(x, y, NORMAL_SPECKLE_RADIUS)); infraredBackgroundGroup.getChildren().add(createSpeckle(x, y, INFRARED_SPECKLE_RADIUS)); } backgroundTexture = new WritableImage(tileSize, tileSize); offScreenBackgroundScene.snapshot(backgroundTexture); infraredBackgroundTexture = new WritableImage(tileSize, tileSize); offScreenInfraredBackgroundScene.snapshot(infraredBackgroundTexture); } private Shape createSpeckle(int x, int y, double radius) { Shape result = new Circle(radius); result.setFill(SPECKLE_COLOR); result.getTransforms().add(new Translate(x, y)); return result; } private int getRandomCoordinate(int maxSize) { double maxSpeckleRadius = Math.max(NORMAL_SPECKLE_RADIUS, INFRARED_SPECKLE_RADIUS); // Don't get too close to the edges of the tile - we don't want half-speckles return (int) (maxSpeckleRadius + Math.random() * (maxSize - (maxSpeckleRadius * 2))); } }