Skip to content

Instantly share code, notes, and snippets.

@bmorris591
Last active March 18, 2017 15:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bmorris591/d631b56bf4ee6ce8fe3b42227e8dcea0 to your computer and use it in GitHub Desktop.
Save bmorris591/d631b56bf4ee6ce8fe3b42227e8dcea0 to your computer and use it in GitHub Desktop.
Little JavaFX application to generate XKCD-like self descriptive image
.axis-tick-mark,
.axis-minor-tick-mark,
.chart-vertical-grid-lines,
.chart-horizontal-grid-lines,
.chart-vertical-zero-line,
.chart-horizontal-zero-line {
-fx-stroke: transparent;
}
.chart-alternative-row-fill, .chart-alternative-column-fill{
-fx-fill: white;
}
.chart-plot-background {
-fx-background-color: white;
}
.chart-vertical-grid-lines {
-fx-stroke: white;
}
.chart-horizontal-grid-lines {
-fx-stroke: white;
}
.chart-alternative-row-fill {
-fx-fill: white;
-fx-stroke: transparent;
-fx-stroke-width: 0;
}
/* Colors of JavaFX pie chart slices. */
.data0.chart-pie {
-fx-pie-color: white;
}
.data1.chart-pie {
-fx-pie-color: black;
}
.chart-pie {
-fx-border-color: black;
-fx-border-width: 2px;
-fx-background-color: derive(-fx-pie-color,0%);
}
package com.boris.testbench;/**
* Created by Boris on 18/03/2017.
*/
import java.io.IOException;
import java.util.stream.Stream;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.SnapshotParameters;
import javafx.scene.chart.BarChart;
import javafx.scene.chart.CategoryAxis;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.PieChart;
import javafx.scene.chart.XYChart;
import javafx.scene.effect.ColorAdjust;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.image.PixelReader;
import javafx.scene.image.WritableImage;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.CornerRadii;
import javafx.scene.layout.HBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;
import lombok.ToString;
public class SelfDescription extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage stage) throws IOException {
ColorAdjust grayscale = new ColorAdjust();
grayscale.setSaturation(-1);
ObservableList<PieChart.Data> pieChartData =
FXCollections.observableArrayList(
new PieChart.Data("White", 50),
new PieChart.Data("Black", 50));
final PieChart chart = new PieChart(pieChartData);
chart.setBackground(new Background(new BackgroundFill(Color.WHITE, CornerRadii.EMPTY, Insets.EMPTY)));
chart.setLegendVisible(false);
chart.getStylesheets().add("/com/boris/testbench/pie.css");
final CategoryAxis xAxis = new CategoryAxis();
final NumberAxis yAxis = new NumberAxis();
yAxis.setTickLabelsVisible(false);
final BarChart<String, Number> bc = new BarChart<>(xAxis, yAxis);
yAxis.setLabel("Amount of black in by panel");
XYChart.Series<String, Number> series1 = new XYChart.Series<>();
series1.getData().add(new XYChart.Data<>("1", 0.33));
series1.getData().add(new XYChart.Data<>("2", 0.33));
series1.getData().add(new XYChart.Data<>("3", 0.33));
bc.setLegendVisible(false);
bc.setBackground(new Background(new BackgroundFill(Color.WHITE, CornerRadii.EMPTY, Insets.EMPTY)));
bc.setBarGap(-1);
bc.setCategoryGap(50);
bc.getData().addAll(series1);
for (Node n : bc.lookupAll(".default-color0.chart-bar")) {
n.setStyle("-fx-bar-fill: black;");
}
Rectangle r = new Rectangle();
r.setHeight(500);
r.setWidth(500);
r.setFill(Color.WHITE);
r.setStroke(Color.BLACK);
r.setStrokeWidth(2);
HBox root = new HBox();
root.setAlignment(Pos.CENTER);
root.setStyle(
"-fx-background-color: white;" +
" -fx-padding: 15px;" +
" -fx-spacing: 10px;" +
" -fx-border-color: black;" +
" -fx-border-insets: 10px;" +
" -fx-border-style: solid;" +
" -fx-border-width: 2px");
Stream.of(chart, bc, r).forEach(n -> n.setStyle(
"-fx-background-color: white;" +
" -fx-border-color: black;" +
" -fx-border-style: solid;" +
" -fx-border-width: 2px"));
chart.getStylesheets().add("/com/boris/testbench/chart.css");
bc.getStylesheets().add("/com/boris/testbench/chart.css");
root.getChildren().addAll(chart, bc, r);
Scene scene = new Scene(root, Color.WHITE);
scene.getRoot().setEffect(grayscale);
stage.setTitle("Self Description");
stage.setScene(scene);
stage.show();
final ImageView recurFinal = generateLastPanel(root, scene);
Timeline fiveSecondsWonder = new Timeline(new KeyFrame(Duration.seconds(1), new EventHandler<ActionEvent>() {
WritableImage snap = null;
ImageView lastPanel = recurFinal;
@Override
public void handle(ActionEvent event) {
final BlackAndWhile total = calculate(scene.snapshot(snap));
BlackAndWhile pieSnap = calculate(chart.snapshot(new SnapshotParameters(), snap));
BlackAndWhile barSnap = calculate(bc.snapshot(new SnapshotParameters(), snap));
BlackAndWhile recurSnap = calculate(lastPanel.snapshot(new SnapshotParameters(), snap));
pieChartData.get(0).setPieValue(total.whiteRatio() * 100);
pieChartData.get(1).setPieValue(total.blackRatio() * 100);
series1.getData().get(0).setYValue(pieSnap.black / total.black);
series1.getData().get(1).setYValue(barSnap.black / total.black);
series1.getData().get(2).setYValue(recurSnap.black / total.black);
lastPanel = generateLastPanel(root, scene);
}
}));
fiveSecondsWonder.setCycleCount(Timeline.INDEFINITE);
fiveSecondsWonder.play();
}
private ImageView generateLastPanel(HBox root, Scene scene) {
root.getChildren().remove(2);
Rectangle r = new Rectangle();
r.setHeight(500);
r.setWidth(500);
r.setFill(Color.WHITE);
r.setStroke(Color.BLACK);
r.setStrokeWidth(2);
root.getChildren().add(r);
ImageView imageView = null;
for (int j = 0; j < 10; j++) {
WritableImage writableImage = scene.snapshot(null);
imageView = new ImageView(writableImage);
imageView.setPreserveRatio(true);
imageView.setFitWidth(500);
root.getChildren().remove(2);
root.getChildren().add(imageView);
}
return imageView;
}
private static BlackAndWhile calculate(final Image image) {
final BlackAndWhile blackAndWhile = new BlackAndWhile();
final PixelReader pixelReader = image.getPixelReader();
for (int j = 0; j < image.getHeight(); j++) {
for (int k = 0; k < image.getWidth(); k++) {
final Color colour = pixelReader.getColor(k, j);
if (colour.equals(Color.BLACK)) {
blackAndWhile.black++;
} else if (colour.equals(Color.WHITE)) {
blackAndWhile.white++;
}
}
}
return blackAndWhile;
}
@ToString(of = {"black", "white"})
private static final class BlackAndWhile {
double black;
double white;
double blackRatio() {
return black / (black + white);
}
double whiteRatio() {
return white / (black + white);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment