120 |
128 | {harmonyPairs.map((harmony, i) => {
129 | const [r, g, b] = hsv2rgb(harmony.hue, harmony.saturation, harmony.value);
130 | return (
131 |
146 | )
147 | })}
148 |
149 |
175 |
176 |
177 | );
178 | };
179 |
--------------------------------------------------------------------------------
/src/utils.ts:
--------------------------------------------------------------------------------
1 | export const harmonies = {
2 | triad: [120, 240],
3 | tetradic: [60, 180, 240],
4 | complementary: [180],
5 | analogous: [-30, 30],
6 | square: [90, 180, 270]
7 | } as const;
8 |
9 | export const xy2polar = (x: number, y: number): [number, number] => {
10 | let r = Math.sqrt(x * x + y * y);
11 | let phi = Math.atan2(y, x);
12 | return [r, phi];
13 | }
14 |
15 | export const polar2xy = (r: number, phi: number): [number, number] => {
16 | let x = r * Math.cos(phi);
17 | let y = r * Math.sin(phi);
18 | return [x, y];
19 | }
20 |
21 | export const rad2deg = (rad: number) => {
22 | return ((rad + Math.PI) / (2 * Math.PI)) * 360;
23 | }
24 |
25 | export const deg2rad = (hue: number) => {
26 | return hue * (Math.PI / 180);
27 | }
28 |
29 | // hue in range [0, 360]
30 | // saturation, value in range [0,1]
31 | // return [r,g,b] each in range [0,255]
32 | // See: https://en.wikipedia.org/wiki/HSL_and_HSV#From_HSV
33 | export const hsv2rgb = (hue: number, saturation: number, value: number): [number, number, number] => {
34 | let chroma = value * saturation;
35 | let hue1 = hue / 60;
36 | let x = chroma * (1 - Math.abs((hue1 % 2) - 1));
37 | let r1: number = 0,
38 | g1: number = 0,
39 | b1: number = 0;
40 | if (hue1 >= 0 && hue1 <= 1) {
41 | [r1, g1, b1] = [chroma, x, 0];
42 | } else if (hue1 >= 1 && hue1 <= 2) {
43 | [r1, g1, b1] = [x, chroma, 0];
44 | } else if (hue1 >= 2 && hue1 <= 3) {
45 | [r1, g1, b1] = [0, chroma, x];
46 | } else if (hue1 >= 3 && hue1 <= 4) {
47 | [r1, g1, b1] = [0, x, chroma];
48 | } else if (hue1 >= 4 && hue1 <= 5) {
49 | [r1, g1, b1] = [x, 0, chroma];
50 | } else if (hue1 >= 5 && hue1 <= 6) {
51 | [r1, g1, b1] = [chroma, 0, x];
52 | }
53 |
54 | let m = value - chroma;
55 | let [r, g, b] = [r1 + m, g1 + m, b1 + m];
56 |
57 | // Change r,g,b values from [0,1] to [0,255]
58 | return [255 * r, 255 * g, 255 * b];
59 | }
60 |
61 | export const xy2rgb = (x: number, y: number, radius: number) => {
62 | x -= radius;
63 | y -= radius;
64 |
65 | const [r, phi] = xy2polar(x, y);
66 |
67 | const hue = rad2deg(phi);
68 | const saturation = r / radius;
69 | const value = 1.0;
70 |
71 | return hsv2rgb(hue, saturation, value);
72 | }
73 |
74 | export const hsv2xy = (hue: number, saturation: number, value: number, radius: number) => {
75 | const adjustedHue = hue - 180;
76 | const [r, phi] = polar2xy(radius * saturation, deg2rad(adjustedHue));
77 | return {
78 | x: r + radius,
79 | y: phi + radius
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "esModuleInterop": true,
4 | "jsx": "react-jsx",
5 | "module": "ESNext",
6 | "moduleResolution": "Bundler",
7 | "skipLibCheck": true,
8 | "strict": true,
9 | "noEmit": true
10 | },
11 | "include": ["src/**/*"]
12 | }
--------------------------------------------------------------------------------
/website/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "next/core-web-vitals"
3 | }
4 |
--------------------------------------------------------------------------------
/website/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 | .yarn/install-state.gz
8 |
9 | # testing
10 | /coverage
11 |
12 | # next.js
13 | /.next/
14 | /out/
15 |
16 | # production
17 | /build
18 |
19 | # misc
20 | .DS_Store
21 | *.pem
22 |
23 | # debug
24 | npm-debug.log*
25 | yarn-debug.log*
26 | yarn-error.log*
27 |
28 | # local env files
29 | .env*.local
30 |
31 | # vercel
32 | .vercel
33 |
34 | # typescript
35 | *.tsbuildinfo
36 | next-env.d.ts
37 |
--------------------------------------------------------------------------------
/website/README.md:
--------------------------------------------------------------------------------
1 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
2 |
3 | ## Getting Started
4 |
5 | First, run the development server:
6 |
7 | ```bash
8 | npm run dev
9 | # or
10 | yarn dev
11 | # or
12 | pnpm dev
13 | # or
14 | bun dev
15 | ```
16 |
17 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
18 |
19 | You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
20 |
21 | This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
22 |
23 | ## Learn More
24 |
25 | To learn more about Next.js, take a look at the following resources:
26 |
27 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
28 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
29 |
30 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
31 |
32 | ## Deploy on Vercel
33 |
34 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
35 |
36 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
37 |
--------------------------------------------------------------------------------
/website/next.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('next').NextConfig} */
2 | const nextConfig = {}
3 |
4 | module.exports = nextConfig
5 |
--------------------------------------------------------------------------------
/website/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "harmony",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint"
10 | },
11 | "dependencies": {
12 | "@newfrgmnt/harmony": "^1.0.3",
13 | "@vercel/analytics": "^1.1.1",
14 | "framer-motion": "^10.18.0",
15 | "next": "14.0.4",
16 | "react": "^18",
17 | "react-dom": "^18"
18 | },
19 | "devDependencies": {
20 | "@types/node": "^20",
21 | "@types/react": "^18",
22 | "@types/react-dom": "^18",
23 | "autoprefixer": "^10.0.1",
24 | "eslint": "^8",
25 | "eslint-config-next": "14.0.4",
26 | "postcss": "^8",
27 | "tailwindcss": "^3.3.0",
28 | "typescript": "^5"
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/website/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/website/public/next.svg:
--------------------------------------------------------------------------------
1 |