compare-image/src/main/kotlin/io/gitlab/jfronny/compareimage/HelloApplication.kt

125 lines
4.5 KiB
Kotlin

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<PickerController>()
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<String, Number>()
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<String>) {
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")
))
}