package io.gitlab.jfronny.compareimage import javafx.application.Application import javafx.collections.FXCollections import javafx.fxml.FXMLLoader import javafx.scene.Scene import javafx.scene.SnapshotParameters import javafx.scene.chart.CategoryAxis import javafx.scene.chart.NumberAxis import javafx.scene.chart.StackedBarChart import javafx.scene.chart.XYChart import javafx.scene.chart.XYChart.Series import javafx.scene.image.WritableImage import javafx.stage.Stage import java.awt.image.BufferedImage import java.nio.file.Files import java.nio.file.Path import java.util.* import javax.imageio.ImageIO import kotlin.io.path.Path import kotlin.io.path.isDirectory import kotlin.io.path.listDirectoryEntries import kotlin.io.path.outputStream class HelloApplication : Application() { override fun start(stage: Stage) { // Establish a task queue val queue: Queue<() -> Unit> = LinkedList() val immediate: Queue<() -> Unit> = LinkedList() fun update() { while (immediate.isNotEmpty()) immediate.remove()() if (queue.isNotEmpty()) queue.remove()() } // Get data from parameters val options = parameters.raw.map { Path(it) }.map { PickerOption(it) } val permutations = options.run { flatMapIndexed { i, left -> subList(1 + i, size).map { right -> left to right } } } // Show screen for single decision permutations.forEachIndexed { i, it -> queue.add { if (it.first.score < -3 || it.second.score < -3) { update() return@add } val fxmlLoader = FXMLLoader(HelloApplication::class.java.getResource("picker-view.fxml")) val scene = Scene(fxmlLoader.load(), 320.0, 240.0) val controller = fxmlLoader.getController() val permutationStage = Stage() controller.hydrate(it.first, it.second, close = { permutationStage.close() update() }) permutationStage.setOnCloseRequest { update() } permutationStage.title = "${i + 1} / ${permutations.size}" permutationStage.scene = scene permutationStage.show() immediate.add { val s = when (controller.state) { PickedState.LEFT -> it.first to it.second PickedState.RIGHT -> it.second to it.first else -> null } if (s != null) { s.first.score++ s.second.score-- } } } } // Show tally screen queue.add { val data = options.toList() .filter { it.score >= 0 } .sortedByDescending { it.score } .map { it.file.fileName.toString() to it.score } val xAxis = CategoryAxis() val yAxis = NumberAxis() val sbc = StackedBarChart(xAxis, yAxis) val series = Series() stage.title = "Tally" sbc.title = "Tally" xAxis.label = "File" xAxis.categories = FXCollections.observableArrayList(data.map { it.first }) yAxis.label = "Value" series.name = "Files" series.data.addAll(data.map { XYChart.Data(it.first, it.second) }) val scene = Scene(sbc, 800.0, 600.0) sbc.data.addAll(series) val path = Files.createTempFile("compare-image-", ".png") writeImage(sbc.snapshot(SnapshotParameters(), null), path) println("Wrote image to: $path") stage.scene = scene stage.show() } update() } } fun writeImage(image: WritableImage, path: Path) { val buf = BufferedImage(image.width.toInt(), image.height.toInt(), BufferedImage.TYPE_INT_ARGB) val reader = image.pixelReader for (x in 0 until buf.width) { for (y in 0 until buf.height) { buf.setRGB(x, y, reader.getArgb(x, y)) } } path.outputStream().use { ImageIO.write(buf, "png", it) } } fun main(args: Array) { Application.launch(HelloApplication::class.java, *( if (args.size > 1) args else if (args.size == 1 && Path(args[0]).isDirectory()) Path(args[0]).listDirectoryEntries().map { it.toString() }.toTypedArray() else throw IllegalArgumentException("Please specify the images to compare") )) }