@rific/auto-paper 0.7.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,103 @@
1
+ import { getHex } from './getHex'
2
+ import { getRgb } from './getRgb'
3
+
4
+ type HSL = { h: number; s: number; l: number }
5
+
6
+ function rgbToHsl(r: number, g: number, b: number): HSL {
7
+ r /= 255
8
+ g /= 255
9
+ b /= 255
10
+
11
+ const max = Math.max(r, g, b)
12
+ const min = Math.min(r, g, b)
13
+ const delta = max - min
14
+
15
+ let h = 0
16
+ if (delta !== 0) {
17
+ if (max === r) h = ((g - b) / delta) % 6
18
+ else if (max === g) h = (b - r) / delta + 2
19
+ else h = (r - g) / delta + 4
20
+ h = (h * 60 + 360) % 360
21
+ }
22
+
23
+ const l = (max + min) / 2
24
+ const s = delta === 0 ? 0 : delta / (1 - Math.abs(2 * l - 1))
25
+
26
+ return { h, s, l }
27
+ }
28
+
29
+ function hslToHex(h: number, s: number, l: number): string {
30
+ const c = (1 - Math.abs(2 * l - 1)) * s
31
+ const x = c * (1 - Math.abs(((h / 60) % 2) - 1))
32
+ const m = l - c / 2
33
+
34
+ let r = 0
35
+ let g = 0
36
+ let b = 0
37
+
38
+ if (h < 60) {
39
+ r = c
40
+ g = x
41
+ b = 0
42
+ } else if (h < 120) {
43
+ r = x
44
+ g = c
45
+ b = 0
46
+ } else if (h < 180) {
47
+ r = 0
48
+ g = c
49
+ b = x
50
+ } else if (h < 240) {
51
+ r = 0
52
+ g = x
53
+ b = c
54
+ } else if (h < 300) {
55
+ r = x
56
+ g = 0
57
+ b = c
58
+ } else {
59
+ r = c
60
+ g = 0
61
+ b = x
62
+ }
63
+
64
+ const toInt = (value: number) => Math.round((value + m) * 255)
65
+ return getHex(`rgb(${toInt(r)}, ${toInt(g)}, ${toInt(b)})`) ?? '#000000'
66
+ }
67
+
68
+ const toRad = (deg: number) => (deg * Math.PI) / 180
69
+ const toDeg = (rad: number) => (rad * 180) / Math.PI
70
+
71
+ // The hue maximally distant from both inputs: the midpoint of the longer arc between them.
72
+ // Falls back to a perpendicular hue when the inputs are exact complements, since both arcs are
73
+ // equal length there, so every point on the perpendicular bisector is equally valid.
74
+ function thirdHue(hA: number, hB: number): number {
75
+ const x = Math.cos(toRad(hA)) + Math.cos(toRad(hB))
76
+ const y = Math.sin(toRad(hA)) + Math.sin(toRad(hB))
77
+
78
+ if (Math.abs(x) < 1e-9 && Math.abs(y) < 1e-9) return (hA + 90) % 360
79
+
80
+ const meanHue = (toDeg(Math.atan2(y, x)) + 360) % 360
81
+ return (meanHue + 180) % 360
82
+ }
83
+
84
+ // Given two arbitrary colors, derives a third that's maximally distinct in hue from both, useful
85
+ // for a neutral element that shouldn't visually clash with either. Saturation and lightness are
86
+ // averaged from the two inputs.
87
+ export function getThirdColor(colorA: string, colorB: string): string {
88
+ const rgbA = getRgb(colorA)
89
+ const rgbB = getRgb(colorB)
90
+ if (!rgbA) throw new Error(`Invalid color format: ${colorA}`)
91
+ if (!rgbB) throw new Error(`Invalid color format: ${colorB}`)
92
+
93
+ const hslA = rgbToHsl(rgbA.r, rgbA.g, rgbA.b)
94
+ const hslB = rgbToHsl(rgbB.r, rgbB.g, rgbB.b)
95
+
96
+ const h = thirdHue(hslA.h, hslB.h)
97
+ const s = (hslA.s + hslB.s) / 2
98
+ const l = (hslA.l + hslB.l) / 2
99
+
100
+ return hslToHex(h, s, l)
101
+ }
102
+
103
+ export default getThirdColor