diff --git a/apply/application.ts b/apply/application.ts index 576457a..e746f1e 100644 --- a/apply/application.ts +++ b/apply/application.ts @@ -1,4 +1,5 @@ import type { ImageAnalysisData } from "../analyze/analysis.ts" +import * as colourDiff from "color-diff" export interface ApplicationConfig { latitude: number @@ -77,19 +78,25 @@ export function findMatchingImages( chromaValue: number, lightnessValue: number, ) { - const targetColour = [lightnessValue, chromaValue, hueValue] - let closestColour = imagesData.files[0] + // let's convert Oklch to CIELAB first: + const aValue = chromaValue * Math.cos(hueValue) + const bValue = chromaValue * Math.sin(hueValue) - for (const file of imagesData.files) { - if ( - Math.abs(file.oklch[0] - targetColour[0]) <= Math.abs(closestColour.oklch[0] - targetColour[0]) && - Math.abs(file.oklch[1] - targetColour[1]) <= Math.abs(closestColour.oklch[1] - targetColour[1]) && - Math.abs(file.oklch[2] - targetColour[2]) <= Math.abs(closestColour.oklch[2] - targetColour[2]) - ) { - closestColour = file - } - } - console.info(`Found target image ${closestColour.path} with colour oklch(${closestColour.oklch[0]} ${closestColour.oklch[1]} ${closestColour.oklch[2]})!`) + const matchingImages = [] - return closestColour + let targetDifference = 0.07 + while (matchingImages.length == 0) { + for (const image of imagesData.files) { + const targetAValue = image.oklch[1] * Math.cos(image.oklch[2]) + const targetBValue = image.oklch[1] * Math.sin(image.oklch[2]) + + // then use the CIEDE2000 algorithm to calculate the difference between the colours + const difference = colourDiff.diff({L: lightnessValue, a: aValue, b: bValue}, {L: image.oklch[0], a: targetAValue, b: targetBValue}) + if (difference <= targetDifference) { + matchingImages.push(image) + } + } + targetDifference += 0.01 + } + return matchingImages } diff --git a/apply/index.ts b/apply/index.ts index 18dfadf..3730b54 100644 --- a/apply/index.ts +++ b/apply/index.ts @@ -1,8 +1,8 @@ #!/usr/bin/env zx -import { ImageAnalysisData } from "../analyze/analysis.ts" -import { cacheDir, chalkDebug, configDir, loadConfig, map, mapEaseOutQuint } from "../utils.ts" -import { ApplicationConfig, defaultConfig, findMatchingImages, getOpenMeteoData } from "./application.ts" +import type { ImageAnalysisData } from "../analyze/analysis.ts" +import { cacheDir, chalkDebug, configDir, loadConfig, map, mapEaseOutQuint, mapEaseOutSqrt } from "../utils.ts" +import { type ApplicationConfig, defaultConfig, findMatchingImages, getOpenMeteoData } from "./application.ts" import * as SunCalc from "suncalc" import "zx/globals" @@ -57,7 +57,7 @@ const chromaValue = map( config.chromaRange.end, ) -const lightnessValue = map( +const lightnessValue = mapEaseOutSqrt( Number(openMeteoData.shortwaveRadiation), 0, 1000, @@ -74,8 +74,10 @@ const cacheFile = await fs.promises.readFile(`${pathToCache}`, { const imagesData = JSON.parse(cacheFile) as ImageAnalysisData console.debug(chalkDebug(`Opened cache file ${pathToCache}!`)) -const targetFile = findMatchingImages(imagesData, hueValue, chromaValue, lightnessValue) +const targetImages = findMatchingImages(imagesData, hueValue, chromaValue, lightnessValue) +const targetImage = targetImages[Math.floor(Math.random() * targetImages.length)] +console.info(`Found target image ${targetImage.path} with colour oklch(${targetImage.oklch[0]} ${targetImage.oklch[1]} ${targetImage.oklch[2]})!`) console.info(`Setting wallpaper...`) -await $`eval ${config.applyWallpaperCommand.replace("%s", targetFile.path)}` +await $`eval ${config.applyWallpaperCommand.replace("%s", targetImage.path)}` console.info(`Success! Enjoy :)`) diff --git a/deno.json b/deno.json index 89275c3..e050cc5 100644 --- a/deno.json +++ b/deno.json @@ -9,8 +9,8 @@ "tasks": { "compile-analyze": "deno compile --output build/rainwall-analyze -A analyze/index.ts", "compile-apply": "deno compile --output build/rainwall-apply -A apply/index.ts", - "analyze": "deno run -A analyze/index.ts", - "apply": "deno run -A apply/index.ts" + "analyze": "deno -A analyze/index.ts", + "apply": "deno -A apply/index.ts" }, "imports": { "@std/assert": "jsr:@std/assert@1", diff --git a/deno.lock b/deno.lock index 67695d1..1383065 100644 --- a/deno.lock +++ b/deno.lock @@ -5,6 +5,8 @@ "jsr:@std/internal@^1.0.12": "1.0.14", "jsr:@std/internal@^1.0.14": "1.0.14", "jsr:@std/path@^1.1.5": "1.1.5", + "npm:chalk@^5.6.2": "5.6.2", + "npm:color-diff@^1.4.0": "1.4.0", "npm:suncalc@2": "2.0.0", "npm:zx@^8.8.5": "8.8.5" }, @@ -26,6 +28,12 @@ } }, "npm": { + "chalk@5.6.2": { + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==" + }, + "color-diff@1.4.0": { + "integrity": "sha512-4oDB/o78lNdppbaqrg0HjOp7pHmUc+dfCxWKWFnQg6AB/1dkjtBDop3RZht5386cq9xBUDRvDvSCA7WUlM9Jqw==" + }, "suncalc@2.0.0": { "integrity": "sha512-RcO28GOMXNnB+sr+c7/+zv5UPMa9wxIsUVGE3HddjQykqCVEC2rc4t4IjBB7qaa7K7dx5Cxp8UZwexyXrX8JkA==" }, @@ -42,6 +50,7 @@ "packageJson": { "dependencies": [ "npm:chalk@^5.6.2", + "npm:color-diff@^1.4.0", "npm:suncalc@2", "npm:zx@^8.8.5" ] diff --git a/package-lock.json b/package-lock.json index 098c25b..c5490f9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,6 +6,7 @@ "": { "dependencies": { "chalk": "^5.6.2", + "color-diff": "^1.4.0", "suncalc": "^2.0.0", "zx": "^8.8.5" } @@ -22,6 +23,12 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/color-diff": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/color-diff/-/color-diff-1.4.0.tgz", + "integrity": "sha512-4oDB/o78lNdppbaqrg0HjOp7pHmUc+dfCxWKWFnQg6AB/1dkjtBDop3RZht5386cq9xBUDRvDvSCA7WUlM9Jqw==", + "license": "BSD-3-Clause" + }, "node_modules/suncalc": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/suncalc/-/suncalc-2.0.0.tgz", diff --git a/package.json b/package.json index 1c5ec4f..bc8d848 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,7 @@ { "dependencies": { "chalk": "^5.6.2", + "color-diff": "^1.4.0", "suncalc": "^2.0.0", "zx": "^8.8.5" } diff --git a/utils.ts b/utils.ts index 17d4d55..8cb46d6 100644 --- a/utils.ts +++ b/utils.ts @@ -88,3 +88,11 @@ function easeOutQuint(x: number): number { export function mapEaseOutQuint(number: number, min: number, max: number, newMin: number, newMax: number) { return easeOutQuint((number - min) / (max - min)) * (newMax - newMin) + newMin } + +function easeOutSqrt(x: number): number { + return Math.sqrt(x) +} + +export function mapEaseOutSqrt(number: number, min: number, max: number, newMin: number, newMax: number) { + return easeOutSqrt((number - min) / (max - min)) * (newMax - newMin) + newMin +} \ No newline at end of file