From 88755feac43963c31919d4dfc45e0c4515c21897 Mon Sep 17 00:00:00 2001
From: Mike Lynch <m.lynch@sydney.edu.au>
Date: Sat, 15 Mar 2025 13:47:03 +1100
Subject: [PATCH 1/2] Writing my own colour name picker

---
 package-lock.json          | 20 ++++++++++++++------
 package.json               |  2 +-
 poptimal.js                | 17 ++++++++---------
 src/components/palettes.js | 34 +++++++++++++++++++++++++++++++++-
 4 files changed, 56 insertions(+), 17 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index 7e6b89a..9051999 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,9 +9,9 @@
         "@resvg/resvg-js": "^2.6.2",
         "d3": "^7.9.0",
         "d3-color": "^3.1.0",
+        "d3-color-difference": "^0.1.3",
         "d3-dsv": "^3.0.1",
         "d3-time-format": "^4.1.0",
-        "hex-color-to-color-name": "^1.0.2",
         "jsdom": "^26.0.0",
         "lodash.shuffle": "^4.2.0",
         "random": "^5.1.1",
@@ -1766,6 +1766,19 @@
         "node": ">=12"
       }
     },
+    "node_modules/d3-color-difference": {
+      "version": "0.1.3",
+      "resolved": "https://registry.npmjs.org/d3-color-difference/-/d3-color-difference-0.1.3.tgz",
+      "integrity": "sha512-yAGiVdTZR/wpI66n85xvkTvjxFth0IuGrEeX/anl1Q5rzNc2/V7oOjoJdqQnahOuS+SgbAR0zu8T0SYY7hGTtw==",
+      "dependencies": {
+        "d3-color": "^1.1.0"
+      }
+    },
+    "node_modules/d3-color-difference/node_modules/d3-color": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-1.4.1.tgz",
+      "integrity": "sha512-p2sTHSLCJI2QKunbGb7ocOh7DgTAn8IrLx21QRc/BSnodXM4sv6aLQlnfpvehFMLZEfBc6g9pH9SWQccFYfJ9Q=="
+    },
     "node_modules/d3-contour": {
       "version": "4.0.2",
       "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.2.tgz",
@@ -2482,11 +2495,6 @@
         "he": "bin/he"
       }
     },
-    "node_modules/hex-color-to-color-name": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/hex-color-to-color-name/-/hex-color-to-color-name-1.0.2.tgz",
-      "integrity": "sha512-YKPBFTSbYIHH8YKcJB4Q5PV+Tr+kvDXpV60BcPMUu5CSZUcc/qOOx7lkr7luk6MSXKd5A82yfPGZTgedIdQ+aA=="
-    },
     "node_modules/highlight.js": {
       "version": "11.11.1",
       "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.11.1.tgz",
diff --git a/package.json b/package.json
index 58600cf..3fb1144 100644
--- a/package.json
+++ b/package.json
@@ -13,9 +13,9 @@
     "@resvg/resvg-js": "^2.6.2",
     "d3": "^7.9.0",
     "d3-color": "^3.1.0",
+    "d3-color-difference": "^0.1.3",
     "d3-dsv": "^3.0.1",
     "d3-time-format": "^4.1.0",
-    "hex-color-to-color-name": "^1.0.2",
     "jsdom": "^26.0.0",
     "lodash.shuffle": "^4.2.0",
     "random": "^5.1.1",
diff --git a/poptimal.js b/poptimal.js
index 187517c..a98fcf1 100644
--- a/poptimal.js
+++ b/poptimal.js
@@ -12,9 +12,7 @@ const xlinkns = "http://www.w3.org/1999/xlink";
 const svgns = "http://www.w3.org/2000/svg";
 
 import {RADIUS_OPTS, DotMaker} from './src/components/dots.js';
-import {PALETTES} from './src/components/palettes.js';
-
-import { GetColorName } from "hex-color-to-color-name";
+import {PALETTES, colour_to_text} from './src/components/palettes.js';
 
 const CELL = 10;
 const MAG = 2;
@@ -40,12 +38,6 @@ function randomise_params() {
 	}
 }
 
-function colour_to_text(d3color) {
-	const rawname = GetColorName(d3color.formatHex()).toLowerCase();
-	// some return values are things like "cyan / aqua": take the first
-	const parts = rawname.split(/\//);
-	return parts[0].trim();
-} 
 
 function image_description(params) {
 	const bgc = colour_to_text(params.background);
@@ -138,6 +130,13 @@ async function main() {
 		.default('s', 1200)
 		.default('c', 'config.json').argv;
 
+	if( argv.t ) {
+		const blue = d3.rgb(0, 0, 255);
+		const name = await colour_to_text(blue, "./rgb.txt");
+		console.log(name);
+		process.exit();
+	}
+
 	const cfjson = await promises.readFile(argv.c);
   	const cf = JSON.parse(cfjson);
 
diff --git a/src/components/palettes.js b/src/components/palettes.js
index 7d01ac5..3b0db99 100644
--- a/src/components/palettes.js
+++ b/src/components/palettes.js
@@ -2,6 +2,9 @@
 import * as d3 from "d3-color";
 import shuffle from "lodash.shuffle";
 import random from "random";
+import { promises } from "fs";
+
+import * as d3cd from "d3-color-difference";
 
 const PALETTES = new Map([
 	[ "random RGB", palette_random ], 
@@ -75,4 +78,33 @@ function palette_cmy() {
 	return shuffle(cols);
 }
 
-export { PALETTES }
+async function parse_colours(colourfile) {
+	const colourBuff = await promises.readFile(colourfile);
+	const colours = colourBuff.toString();
+	const colmap = new Map();
+	const seen = new Set(); 
+	colours.split(/\n/).map((l) => {
+		const m = l.match(/(\d+)\s+(\d+)\s+(\d+)\s+(.*)/);
+		if( m ) {
+			const sig = `${m[1]},${m[2]},${m[3]}`;
+			if( !seen.has(sig) ) {
+				colmap.set(m[4], d3.rgb(m[1], m[2], m[3]));
+				seen.add(sig);
+			}
+		}
+	});
+	return colmap;
+}
+
+async function colour_to_text(d3color, colourfile) {
+	const colours = await parse_colours(colourfile);
+	const diffs = []
+	for( const [ name, colour] of colours) {
+		diffs.push({name: name, dist: d3cd.differenceCie76(colour, d3color)});
+	}
+	diffs.sort((a, b) => a.dist - b.dist);
+	return diffs[0].name;
+} 
+
+
+export { PALETTES, colour_to_text }

From 469de3c94db840b3064a9431ad4c6663dffce14c Mon Sep 17 00:00:00 2001
From: Mike Lynch <m.lynch@sydney.edu.au>
Date: Sun, 16 Mar 2025 14:46:34 +1100
Subject: [PATCH 2/2] Added my own colour name code to palettes.js

---
 poptimal.js                |  21 ++---
 rgb.txt                    | 169 +++++++++++++++++++++++++++++++++++++
 src/components/palettes.js |  53 ++++++------
 3 files changed, 206 insertions(+), 37 deletions(-)
 create mode 100644 rgb.txt

diff --git a/poptimal.js b/poptimal.js
index a98fcf1..462e19a 100644
--- a/poptimal.js
+++ b/poptimal.js
@@ -12,7 +12,7 @@ const xlinkns = "http://www.w3.org/1999/xlink";
 const svgns = "http://www.w3.org/2000/svg";
 
 import {RADIUS_OPTS, DotMaker} from './src/components/dots.js';
-import {PALETTES, colour_to_text} from './src/components/palettes.js';
+import {PALETTES, ColourNamer} from './src/components/palettes.js';
 
 const CELL = 10;
 const MAG = 2;
@@ -39,9 +39,9 @@ function randomise_params() {
 }
 
 
-function image_description(params) {
-	const bgc = colour_to_text(params.background);
-	const dotc = params.patterns.map((p) => colour_to_text(p.colour));
+function image_description(namer, params) {
+	const bgc = namer.colour_to_text(params.background);
+	const dotc = params.patterns.map((p) => namer.colour_to_text(p.colour));
 	const a = bgc.match(/^[aeiou]/) ? 'an' : 'a';
 	return `A pattern of ${dotc[0]} and ${dotc[1]} dots on ${a} ${bgc} background`;
 
@@ -130,22 +130,19 @@ async function main() {
 		.default('s', 1200)
 		.default('c', 'config.json').argv;
 
-	if( argv.t ) {
-		const blue = d3.rgb(0, 0, 255);
-		const name = await colour_to_text(blue, "./rgb.txt");
-		console.log(name);
-		process.exit();
-	}
-
 	const cfjson = await promises.readFile(argv.c);
   	const cf = JSON.parse(cfjson);
 
+	const namer = new ColourNamer();
+	await namer.load_colours(cf['colours']);
+
+
   	const fn = argv.o || String(Date.now()) + '.png';
 
   	const imgfile = cf['working_dir'] + '/' + fn;
 
 	const params = randomise_params();
-	const alt_text = image_description(params);
+	const alt_text = image_description(namer, params);
 
 	const svg = poptimal_svg(params);
 	const opts = {
diff --git a/rgb.txt b/rgb.txt
new file mode 100644
index 0000000..7af8368
--- /dev/null
+++ b/rgb.txt
@@ -0,0 +1,169 @@
+255 250 250 snow
+248 248 255 ghost white
+245 245 245 white smoke
+220 220 220 gainsboro
+255 250 240 floral white
+253 245 230 old lace
+250 240 230 linen
+250 235 215 antique white
+255 239 213 papaya whip
+255 235 205 blanched almond
+255 228 196 bisque
+255 218 185 peach puff
+255 222 173 navajo white
+255 228 181 moccasin
+255 248 220 cornsilk
+255 255 240 ivory
+255 250 205 lemon chiffon
+255 245 238 seashell
+240 255 240 honeydew
+245 255 250 mint cream
+240 255 255 azure
+240 248 255 alice blue
+230 230 250 lavender
+255 240 245 lavender blush
+255 228 225 misty rose
+255 255 255 white
+105 105 105 dim grey
+112 128 144 slate grey
+119 136 153 light slate grey
+190 190 190 grey
+211 211 211 light grey
+100 149 237 cornflower blue
+106 90 205 slate blue
+123 104 238 medium slate blue
+132 112 255 light slate blue
+135 206 235 sky blue
+135 206 250 light sky blue
+176 196 222 light steel blue
+173 216 230 light blue
+176 224 230 powder blue
+175 238 238 pale turquoise
+224 255 255 light cyan
+102 205 170 medium aquamarine
+127 255 212 aquamarine
+143 188 143 dark sea green
+152 251 152 pale green
+124 252 0 lawn green
+127 255 0 chartreuse
+173 255 47 green yellow
+154 205 50 yellow green
+107 142 35 olive drab
+189 183 107 dark khaki
+240 230 140 khaki
+238 232 170 pale goldenrod
+250 250 210 light goldenrod yellow
+255 255 224 light yellow
+255 255 0 yellow
+255 215 0 gold
+238 221 130 light goldenrod
+218 165 32 goldenrod
+184 134 11 dark goldenrod
+188 143 143 rosy brown
+205 92 92 indian red
+139 69 19 saddle brown
+160 82 45 sienna
+205 133 63 peru
+222 184 135 burlywood
+245 245 220 beige
+245 222 179 wheat
+244 164 96 sandy brown
+210 180 140 tan
+210 105 30 chocolate
+178 34 34 firebrick
+165 42 42 brown
+233 150 122 dark salmon
+250 128 114 salmon
+255 160 122 light salmon
+255 165 0 orange
+255 140 0 dark orange
+255 127 80 coral
+240 128 128 light coral
+255 99 71 tomato
+255 69 0 orange red
+255 0 0 red
+255 105 180 hot pink
+255 20 147 deep pink
+255 192 203 pink
+255 182 193 light pink
+219 112 147 pale violet red
+176 48 96 maroon
+199 21 133 medium violet red
+208 32 144 violet red
+255 0 255 magenta
+238 130 238 violet
+221 160 221 plum
+218 112 214 orchid
+186 85 211 medium orchid
+153 50 204 dark orchid
+148 0 211 dark violet
+138 43 226 blue violet
+160 32 240 purple
+147 112 219 medium purple
+216 191 216 thistle
+102 102 102 60% grey
+105 105 105 59% grey
+107 107 107 58% grey
+110 110 110 57% grey
+112 112 112 56% grey
+115 115 115 55% grey
+117 117 117 54% grey
+120 120 120 53% grey
+122 122 122 52% grey
+125 125 125 51% grey
+127 127 127 50% grey
+130 130 130 49% grey
+133 133 133 48% grey
+135 135 135 47% grey
+138 138 138 46% grey
+140 140 140 45% grey
+143 143 143 44% grey
+145 145 145 43% grey
+148 148 148 42% grey
+150 150 150 41% grey
+153 153 153 40% grey
+156 156 156 39% grey
+158 158 158 38% grey
+161 161 161 37% grey
+163 163 163 36% grey
+166 166 166 35% grey
+168 168 168 34% grey
+171 171 171 33% grey
+173 173 173 32% grey
+176 176 176 31% grey
+179 179 179 30% grey
+181 181 181 29% grey
+184 184 184 28% grey
+186 186 186 27% grey
+189 189 189 26% grey
+191 191 191 25% grey
+194 194 194 24% grey
+196 196 196 23% grey
+199 199 199 22% grey
+201 201 201 21% grey
+204 204 204 20% grey
+207 207 207 19% grey
+209 209 209 18% grey
+212 212 212 17% grey
+214 214 214 16% grey
+217 217 217 15% grey
+219 219 219 14% grey
+222 222 222 13% grey
+224 224 224 12% grey
+227 227 227 11% grey
+229 229 229 10% grey
+232 232 232 9% grey
+235 235 235 8% grey
+237 237 237 7% grey
+240 240 240 6% grey
+242 242 242 5% grey
+245 245 245 4% grey
+247 247 247 3% grey
+250 250 250 2% grey
+252 252 252 1% grey
+169 169 169 dark grey
+0 0 139 dark blue
+0 139 139 dark cyan
+139 0 139 dark magenta
+139 0 0 dark red
+144 238 144 light green
diff --git a/src/components/palettes.js b/src/components/palettes.js
index 3b0db99..e9a2c27 100644
--- a/src/components/palettes.js
+++ b/src/components/palettes.js
@@ -78,33 +78,36 @@ function palette_cmy() {
 	return shuffle(cols);
 }
 
-async function parse_colours(colourfile) {
-	const colourBuff = await promises.readFile(colourfile);
-	const colours = colourBuff.toString();
-	const colmap = new Map();
-	const seen = new Set(); 
-	colours.split(/\n/).map((l) => {
-		const m = l.match(/(\d+)\s+(\d+)\s+(\d+)\s+(.*)/);
-		if( m ) {
-			const sig = `${m[1]},${m[2]},${m[3]}`;
-			if( !seen.has(sig) ) {
-				colmap.set(m[4], d3.rgb(m[1], m[2], m[3]));
-				seen.add(sig);
+class ColourNamer {
+	constructor() {
+		this.colmap = new Map();
+	}
+
+	async load_colours(colourfile) {
+		const colourBuff = await promises.readFile(colourfile);
+		const colours = colourBuff.toString();
+		const seen = new Set(); 
+		colours.split(/\n/).map((l) => {
+			const m = l.match(/(\d+)\s+(\d+)\s+(\d+)\s+(.*)/);
+			if( m ) {
+				const sig = `${m[1]},${m[2]},${m[3]}`;
+				if( !seen.has(sig) ) {
+					this.colmap.set(m[4], d3.rgb(m[1], m[2], m[3]));
+					seen.add(sig);
+				}
 			}
+		});
+	}
+
+	colour_to_text(d3color) {
+		const diffs = []
+		for( const [ name, colour] of this.colmap) {
+			diffs.push({name: name, dist: d3cd.differenceCie76(colour, d3color)});
 		}
-	});
-	return colmap;
+		diffs.sort((a, b) => a.dist - b.dist);
+		return diffs[0].name;
+	}
 }
 
-async function colour_to_text(d3color, colourfile) {
-	const colours = await parse_colours(colourfile);
-	const diffs = []
-	for( const [ name, colour] of colours) {
-		diffs.push({name: name, dist: d3cd.differenceCie76(colour, d3color)});
-	}
-	diffs.sort((a, b) => a.dist - b.dist);
-	return diffs[0].name;
-} 
 
-
-export { PALETTES, colour_to_text }
+export { PALETTES, ColourNamer }