1 module unexpected.rando.palette;
2 
3 import reversineer;
4 import magicalrainbows.formats;
5 import std.random : Random, uniform;
6 
7 import unexpected.rando.common;
8 
9 enum ColourRandomizationLevel {
10 	randomHue,
11 	shiftHue,
12 	multHue,
13 	shiftInverseHue,
14 	multInverseHue,
15 	randomSaturation,
16 	shiftSaturation,
17 	multSaturation,
18 	shiftInverseSaturation,
19 	multInverseSaturation,
20 	randomValue,
21 	shiftValue,
22 	multValue,
23 	shiftInverseValue,
24 	multInverseValue,
25 	absurd,
26 	extreme
27 }
28 
29 T[] randomizePalette(T)(T[] input, ColourRandomizationLevel randomizationLevel, uint seed) {
30 	import std.algorithm.iteration : map;
31 	import std.algorithm.comparison : min;
32 	import std.array : array;
33 	import std.random : Random, uniform01;
34 
35 	auto rand = Random(seed);
36 	const randomConstantH0 = rand.uniform01();
37 	const randomConstantH1 = rand.uniform01();
38 	const randomConstantH2 = rand.uniform01();
39 	const randomConstantS0 = rand.uniform01();
40 	const randomConstantS1 = rand.uniform01();
41 	const randomConstantS2 = rand.uniform01();
42 	const randomConstantV0 = rand.uniform01();
43 	const randomConstantV1 = rand.uniform01();
44 	const randomConstantV2 = rand.uniform01();
45 	HSVA!float genRandomHSV(HSVA!float input, ColourRandomizationLevel level) {
46 		bool shiftHue;
47 		bool multHue;
48 		bool randomizeHue;
49 		bool invertHue;
50 		bool shiftSaturation;
51 		bool multSaturation;
52 		bool randomizeSaturation;
53 		bool invertSaturation;
54 		bool shiftValue;
55 		bool multValue;
56 		bool randomizeValue;
57 		bool invertValue;
58 		bool useRGB;
59 		final switch (randomizationLevel) {
60 			case ColourRandomizationLevel.shiftHue:
61 				shiftHue = true;
62 				break;
63 			case ColourRandomizationLevel.multHue:
64 				multHue = true;
65 				break;
66 			case ColourRandomizationLevel.shiftInverseHue:
67 				invertHue = true;
68 				shiftHue = true;
69 				break;
70 			case ColourRandomizationLevel.multInverseHue:
71 				invertHue = true;
72 				multHue = true;
73 				break;
74 			case ColourRandomizationLevel.randomHue:
75 				randomizeHue = true;
76 				break;
77 			case ColourRandomizationLevel.randomSaturation:
78 				randomizeSaturation = true;
79 				break;
80 			case ColourRandomizationLevel.shiftSaturation:
81 				shiftSaturation = true;
82 				break;
83 			case ColourRandomizationLevel.multSaturation:
84 				multSaturation = true;
85 				break;
86 			case ColourRandomizationLevel.shiftInverseSaturation:
87 				invertSaturation = true;
88 				shiftSaturation = true;
89 				break;
90 			case ColourRandomizationLevel.multInverseSaturation:
91 				invertSaturation = true;
92 				multSaturation = true;
93 				break;
94 			case ColourRandomizationLevel.randomValue:
95 				randomizeValue = true;
96 				break;
97 			case ColourRandomizationLevel.shiftValue:
98 				shiftValue = true;
99 				break;
100 			case ColourRandomizationLevel.multValue:
101 				multValue = true;
102 				break;
103 			case ColourRandomizationLevel.shiftInverseValue:
104 				invertValue = true;
105 				shiftValue = true;
106 				break;
107 			case ColourRandomizationLevel.multInverseValue:
108 				invertValue = true;
109 				multValue = true;
110 				break;
111 			case ColourRandomizationLevel.absurd:
112 				randomizeHue = true;
113 				randomizeSaturation = true;
114 				randomizeValue = true;
115 				break;
116 			case ColourRandomizationLevel.extreme:
117 				useRGB = true;
118 				break;
119 		}
120 		if (useRGB) {
121 			return RGB888(
122 				rand.uniform!ubyte(),
123 				rand.uniform!ubyte(),
124 				rand.uniform!ubyte()
125 			).toHSVA!float;
126 		} else {
127 			float hue = input.hue;
128 			float saturation = input.saturation;
129 			float value = input.value;
130 			if (randomizeHue) {
131 				hue = randomConstantH0;
132 			}
133 			if (randomizeSaturation) {
134 				saturation = randomConstantS0;
135 			}
136 			if (randomizeValue) {
137 				value = randomConstantV0;
138 			}
139 			if (shiftHue) {
140 				hue += randomConstantH1;
141 			}
142 			if (invertHue) {
143 				hue = 1.0 - hue;
144 			}
145 			if (invertSaturation) {
146 				saturation = 1.0 - saturation;
147 			}
148 			if (invertValue) {
149 				value = 1.0 - value;
150 			}
151 			if (multHue) {
152 				hue *= randomConstantH2 * 2.0;
153 			}
154 			if (shiftSaturation) {
155 				hue += randomConstantS1;
156 			}
157 			if (multSaturation) {
158 				hue *= randomConstantS2 * 2.0;
159 			}
160 			if (shiftValue) {
161 				value += randomConstantV1;
162 			}
163 			if (multValue) {
164 				value *= randomConstantV2 * 2.0;
165 			}
166 			return HSVA!float(hue % 1.0, saturation % 1.0, value % 1.0, input.alpha);
167 		}
168 	}
169 	return input.map!(x => x.toHSVA!float).map!(x => genRandomHSV(x, randomizationLevel)).map!(x => x.toRGB!(T, float)).array;
170 }
171 
172 void randomizePalette(Palette paletteOptions, T)(ref T field, ref Random rng, ref uint seed, const Options options) {
173 	if (!paletteOptions.shareSeed) {
174 		seed = rng.uniform!uint;
175 	}
176 	const start = paletteOptions.dontSkipFirst ? 0 : 1;
177 	field[start .. $] = randomizePalette(field[start .. $], options.colourRandomizationStyle, seed);
178 }