5 |
6 |
7 |
8 |
9 |
24 |
25 |
--------------------------------------------------------------------------------
/tea.yaml:
--------------------------------------------------------------------------------
1 | # https://tea.xyz/what-is-this-file
2 | #
3 | # DO NOT REMOVE OR EDIT THIS WARNING:
4 | #
5 | # This file is auto-generated by the TEA app. It is intended to validate ownership of your repository.
6 | # DO NOT commit this file or accept any PR if you don't know what this is.
7 | # We are aware that spammers will try to use this file to try to profit off others' work.
8 | # We take this very seriously and will take action against any malicious actors.
9 | #
10 | # If you are not the owner of this repository, and someone maliciously opens a commit with this file
11 | # please report it to us at support@tea.xyz.
12 | #
13 | # A constitution without this header is invalid.
14 | ---
15 | version: 2.0.0
16 | codeOwners:
17 | - '0x2220923A4190a1A2FEE4545Ad86c15998c58C15B'
18 | quorum: 1
19 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 David Aerne
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/dist/webcomponent.d.ts:
--------------------------------------------------------------------------------
1 | import { Poline, positionFunctions, ColorPoint } from "./index";
2 | export { Poline, positionFunctions };
3 | export declare class PolinePicker extends HTMLElement {
4 | private poline;
5 | private svg;
6 | private interactive;
7 | private line;
8 | private wheel;
9 | private anchors;
10 | private points;
11 | private saturationRings;
12 | private currentPoint;
13 | private allowAddPoints;
14 | private ringAdjust;
15 | private ringHoverIndex;
16 | private boundPointerDown;
17 | private boundPointerMove;
18 | private boundPointerUp;
19 | constructor();
20 | connectedCallback(): void;
21 | disconnectedCallback(): void;
22 | setPoline(poline: Poline): void;
23 | setAllowAddPoints(allow: boolean): void;
24 | addPointAtPosition(x: number, y: number): ColorPoint | null;
25 | private updateLightnessBackground;
26 | private render;
27 | private createSVG;
28 | updateSVG(): void;
29 | private updateSaturationRings;
30 | private describeArc;
31 | private pointToCartesian;
32 | private addEventListeners;
33 | private removeEventListeners;
34 | private handlePointerDown;
35 | private handlePointerMove;
36 | private handlePointerUp;
37 | private pickRing;
38 | private clamp01;
39 | private pointerToNormalizedCoordinates;
40 | private dispatchPolineChange;
41 | }
42 |
--------------------------------------------------------------------------------
/.github/workflows/static.yml:
--------------------------------------------------------------------------------
1 | # Simple workflow for deploying static content to GitHub Pages
2 | name: Deploy static content to Pages
3 |
4 | on:
5 | # Runs on pushes targeting the default branch
6 | push:
7 | branches: ["main"]
8 |
9 | # Allows you to run this workflow manually from the Actions tab
10 | workflow_dispatch:
11 |
12 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
13 | permissions:
14 | contents: read
15 | pages: write
16 | id-token: write
17 |
18 | # Allow one concurrent deployment
19 | concurrency:
20 | group: "pages"
21 | cancel-in-progress: true
22 |
23 | jobs:
24 | # Single deploy job since we're just deploying
25 | deploy:
26 | environment:
27 | name: github-pages
28 | url: ${{ steps.deployment.outputs.page_url }}
29 | runs-on: ubuntu-latest
30 | steps:
31 | - name: Checkout
32 | uses: actions/checkout@v3
33 | - name: Setup Pages
34 | uses: actions/configure-pages@v2
35 | - name: Use Node.js ${{ matrix.node-version }}
36 | uses: actions/setup-node@v3
37 | with:
38 | node-version: ${{ matrix.node-version }}
39 | - run: npm ci
40 | - run: npm run build --if-present
41 | - name: Upload artifact
42 | uses: actions/upload-pages-artifact@v3
43 | with:
44 | # Upload entire repository
45 | path: './dist'
46 | - name: Deploy to GitHub Pages
47 | id: deployment
48 | uses: actions/deploy-pages@v4
49 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "poline",
3 | "version": "0.13.0",
4 | "description": "color palette generator mico-lib",
5 | "type": "module",
6 | "main": "./dist/index.cjs",
7 | "module": "./dist/index.min.mjs",
8 | "browser": "./dist/index.min.js",
9 | "jsdelivr": "./dist/index.umd.js",
10 | "exports": {
11 | ".": {
12 | "require": "./dist/index.cjs",
13 | "import": "./dist/index.mjs",
14 | "types": "./dist/index.d.ts"
15 | },
16 | "./picker": {
17 | "require": "./dist/picker.cjs",
18 | "import": "./dist/picker.mjs",
19 | "types": "./dist/webcomponent.d.ts"
20 | }
21 | },
22 | "types": "dist/index.d.ts",
23 | "scripts": {
24 | "build": "npm test && npm run lint && tsc --build && node ./build.js",
25 | "test": "vitest run",
26 | "lint": "eslint . --ext .ts && npx prettier --check ./src/",
27 | "prettier": "npx prettier --write ./src/",
28 | "bsc": "browser-sync start --server 'dist' --files 'dist'",
29 | "watch": "esbuild ./src/index.ts --bundle --sourcemap --outfile=./dist/index.mjs --format=esm --watch",
30 | "dev": "npm-run-all --parallel watch bsc"
31 | },
32 | "repository": {
33 | "type": "git",
34 | "url": "git+https://github.com/meodai/poline.git"
35 | },
36 | "keywords": [
37 | "color",
38 | "generative-art",
39 | "colour",
40 | "palette-generation",
41 | "generative"
42 | ],
43 | "author": "meodai@gmail.com",
44 | "license": "MIT",
45 | "bugs": {
46 | "url": "https://github.com/meodai/poline/issues"
47 | },
48 | "homepage": "https://github.com/meodai/poline#readme",
49 | "devDependencies": {
50 | "@typescript-eslint/eslint-plugin": "^5.48.0",
51 | "@typescript-eslint/parser": "^5.48.0",
52 | "browser-sync": "^2.27.11",
53 | "esbuild": "^0.16.14",
54 | "eslint": "^8.31.0",
55 | "npm-run-all": "^4.1.5",
56 | "prettier": "^2.8.1",
57 | "typescript": "^4.9.4",
58 | "vitest": "^3.2.4"
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | src/_color.js
2 |
3 | # Logs
4 | logs
5 | *.log
6 | npm-debug.log*
7 | yarn-debug.log*
8 | yarn-error.log*
9 | lerna-debug.log*
10 |
11 | # Diagnostic reports (https://nodejs.org/api/report.html)
12 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
13 |
14 | # Runtime data
15 | pids
16 | *.pid
17 | *.seed
18 | *.pid.lock
19 |
20 | # Directory for instrumented libs generated by jscoverage/JSCover
21 | lib-cov
22 |
23 | # Coverage directory used by tools like istanbul
24 | coverage
25 | *.lcov
26 |
27 | # nyc test coverage
28 | .nyc_output
29 |
30 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
31 | .grunt
32 |
33 | # Bower dependency directory (https://bower.io/)
34 | bower_components
35 |
36 | # node-waf configuration
37 | .lock-wscript
38 |
39 | # Compiled binary addons (https://nodejs.org/api/addons.html)
40 | build/Release
41 |
42 | # Dependency directories
43 | node_modules/
44 | jspm_packages/
45 |
46 | # TypeScript v1 declaration files
47 | typings/
48 |
49 | # TypeScript cache
50 | *.tsbuildinfo
51 |
52 | # Optional npm cache directory
53 | .npm
54 |
55 | # Optional eslint cache
56 | .eslintcache
57 |
58 | # Microbundle cache
59 | .rpt2_cache/
60 | .rts2_cache_cjs/
61 | .rts2_cache_es/
62 | .rts2_cache_umd/
63 |
64 | # Optional REPL history
65 | .node_repl_history
66 |
67 | # Output of 'npm pack'
68 | *.tgz
69 |
70 | # Yarn Integrity file
71 | .yarn-integrity
72 |
73 | # dotenv environment variables file
74 | .env
75 | .env.test
76 |
77 | # parcel-bundler cache (https://parceljs.org/)
78 | .cache
79 |
80 | # Next.js build output
81 | .next
82 |
83 | # Nuxt.js build / generate output
84 | .nuxt
85 |
86 | # Gatsby files
87 | .cache/
88 | # Comment in the public line in if your project uses Gatsby and *not* Next.js
89 | # https://nextjs.org/blog/next-9-1#public-directory-support
90 | # public
91 |
92 | # vuepress build output
93 | .vuepress/dist
94 |
95 | # Serverless directories
96 | .serverless/
97 |
98 | # FuseBox cache
99 | .fusebox/
100 |
101 | # DynamoDB Local files
102 | .dynamodb/
103 |
104 | # TernJS port file
105 | .tern-port
--------------------------------------------------------------------------------
/dist/index.d.ts:
--------------------------------------------------------------------------------
1 | export type FuncNumberReturn = (arg0: number) => Vector2;
2 | export type Vector2 = [number, number];
3 | export type Vector3 = [number, ...Vector2];
4 | export type PartialVector3 = [number | null, number | null, number | null];
5 | /**
6 | * Converts the given (x, y, z) coordinate to an HSL color
7 | * The (x, y) values are used to calculate the hue, while the z value is used as the saturation
8 | * The lightness value is calculated based on the distance of (x, y) from the center (0.5, 0.5)
9 | * Returns an array [hue, saturation, lightness]
10 | * @param xyz:Vector3 [x, y, z] coordinate array in (x, y, z) format (0-1, 0-1, 0-1)
11 | * @returns [hue, saturation, lightness]: Vector3 color array in HSL format (0-360, 0-1, 0-1)
12 | * @example
13 | * pointToHSL([0.5, 0.5, 1]) // [0, 1, 0.5]
14 | * pointToHSL([0.5, 0.5, 0]) // [0, 1, 0]
15 | **/
16 | export declare const pointToHSL: (xyz: [number, number, number], invertedLightness: boolean) => [number, number, number];
17 | /**
18 | * Converts the given HSL color to an (x, y, z) coordinate
19 | * The hue value is used to calculate the (x, y) position, while the saturation value is used as the z coordinate
20 | * The lightness value is used to calculate the distance from the center (0.5, 0.5)
21 | * Returns an array [x, y, z]
22 | * @param hsl:Vector3 [hue, saturation, lightness] color array in HSL format (0-360, 0-1, 0-1)
23 | * @returns [x, y, z]:Vector3 coordinate array in (x, y, z) format (0-1, 0-1, 0-1)
24 | * @example
25 | * hslToPoint([0, 1, 0.5]) // [0.5, 0.5, 1]
26 | * hslToPoint([0, 1, 0]) // [0.5, 0.5, 1]
27 | * hslToPoint([0, 1, 1]) // [0.5, 0.5, 1]
28 | * hslToPoint([0, 0, 0.5]) // [0.5, 0.5, 0]
29 | **/
30 | export declare const hslToPoint: (hsl: [number, number, number], invertedLightness: boolean) => [number, number, number];
31 | export declare const randomHSLPair: (startHue?: number, saturations?: Vector2, lightnesses?: Vector2) => [Vector3, Vector3];
32 | /**
33 | * Clamps an (x, y) position to be within the color wheel circle
34 | * The circle has radius 0.5 centered at (0.5, 0.5)
35 | * If the point is outside the circle, it projects it to the edge
36 | * @param x The x coordinate (0-1)
37 | * @param y The y coordinate (0-1)
38 | * @returns [x, y] clamped to be within the circle
39 | * @example
40 | * clampToCircle(0.5, 0.5) // [0.5, 0.5] - center, unchanged
41 | * clampToCircle(1, 0.5) // [1, 0.5] - edge, unchanged
42 | * clampToCircle(1.5, 0.5) // [1, 0.5] - outside, clamped to edge
43 | */
44 | export declare const clampToCircle: (x: number, y: number) => Vector2;
45 | export declare const randomHSLTriple: (startHue?: number, saturations?: [number, number, number], lightnesses?: [number, number, number]) => [Vector3, Vector3, Vector3];
46 | export type PositionFunction = (t: number, reverse?: boolean) => number;
47 | export declare const positionFunctions: {
48 | linearPosition: PositionFunction;
49 | exponentialPosition: PositionFunction;
50 | quadraticPosition: PositionFunction;
51 | cubicPosition: PositionFunction;
52 | quarticPosition: PositionFunction;
53 | sinusoidalPosition: PositionFunction;
54 | asinusoidalPosition: PositionFunction;
55 | arcPosition: PositionFunction;
56 | smoothStepPosition: PositionFunction;
57 | };
58 | export type ColorPointCollection = {
59 | xyz?: Vector3;
60 | color?: Vector3;
61 | invertedLightness?: boolean;
62 | };
63 | export declare class ColorPoint {
64 | x: number;
65 | y: number;
66 | z: number;
67 | color: Vector3;
68 | private _invertedLightness;
69 | constructor({ xyz, color, invertedLightness, }?: ColorPointCollection);
70 | positionOrColor({ xyz, color, invertedLightness, }: ColorPointCollection): void;
71 | set position([x, y, z]: Vector3);
72 | get position(): Vector3;
73 | set hsl([h, s, l]: Vector3);
74 | get hsl(): Vector3;
75 | get hslCSS(): string;
76 | get oklchCSS(): string;
77 | get lchCSS(): string;
78 | set invertedLightness(val: boolean);
79 | get invertedLightness(): boolean;
80 | shiftHue(angle: number): void;
81 | }
82 | export type PolineOptions = {
83 | anchorColors?: Vector3[];
84 | numPoints?: number;
85 | positionFunction?: (t: number, invert?: boolean) => number;
86 | positionFunctionX?: (t: number, invert?: boolean) => number;
87 | positionFunctionY?: (t: number, invert?: boolean) => number;
88 | positionFunctionZ?: (t: number, invert?: boolean) => number;
89 | invertedLightness?: boolean;
90 | closedLoop?: boolean;
91 | clampToCircle?: boolean;
92 | };
93 | export declare class Poline {
94 | private _anchorPoints;
95 | private _numPoints;
96 | private points;
97 | private _positionFunctionX;
98 | private _positionFunctionY;
99 | private _positionFunctionZ;
100 | private _anchorPairs;
101 | private connectLastAndFirstAnchor;
102 | private _animationFrame;
103 | private _invertedLightness;
104 | private _clampToCircle;
105 | constructor({ anchorColors, numPoints, positionFunction, positionFunctionX, positionFunctionY, positionFunctionZ, closedLoop, invertedLightness, clampToCircle, }?: PolineOptions);
106 | get numPoints(): number;
107 | set numPoints(numPoints: number);
108 | set positionFunction(positionFunction: PositionFunction | PositionFunction[]);
109 | get positionFunction(): PositionFunction | PositionFunction[];
110 | set positionFunctionX(positionFunctionX: PositionFunction);
111 | get positionFunctionX(): PositionFunction;
112 | set positionFunctionY(positionFunctionY: PositionFunction);
113 | get positionFunctionY(): PositionFunction;
114 | set positionFunctionZ(positionFunctionZ: PositionFunction);
115 | get positionFunctionZ(): PositionFunction;
116 | get clampToCircle(): boolean;
117 | set clampToCircle(clamp: boolean);
118 | get anchorPoints(): ColorPoint[];
119 | set anchorPoints(anchorPoints: ColorPoint[]);
120 | updateAnchorPairs(): void;
121 | addAnchorPoint({ xyz, color, insertAtIndex, clamp, }: ColorPointCollection & {
122 | insertAtIndex?: number;
123 | clamp?: boolean;
124 | }): ColorPoint;
125 | removeAnchorPoint({ point, index, }: {
126 | point?: ColorPoint;
127 | index?: number;
128 | }): void;
129 | updateAnchorPoint({ point, pointIndex, xyz, color, clamp, }: {
130 | point?: ColorPoint;
131 | pointIndex?: number;
132 | clamp?: boolean;
133 | } & ColorPointCollection): ColorPoint;
134 | getClosestAnchorPoint({ xyz, hsl, maxDistance, }: {
135 | xyz?: PartialVector3;
136 | hsl?: PartialVector3;
137 | maxDistance?: number;
138 | }): ColorPoint | null;
139 | set closedLoop(newStatus: boolean);
140 | get closedLoop(): boolean;
141 | set invertedLightness(newStatus: boolean);
142 | get invertedLightness(): boolean;
143 | /**
144 | * Returns a flattened array of all points across all segments,
145 | * removing duplicated anchor points at segment boundaries.
146 | *
147 | * Since anchor points exist at both the end of one segment and
148 | * the beginning of the next, this method keeps only one instance of each.
149 | * The filter logic keeps the first point (index 0) and then filters out
150 | * points whose indices are multiples of the segment size (_numPoints),
151 | * which are the anchor points at the start of each segment (except the first).
152 | *
153 | * This approach ensures we get all unique points in the correct order
154 | * while avoiding duplicated anchor points.
155 | *
156 | * @returns {ColorPoint[]} A flat array of unique ColorPoint instances
157 | */
158 | get flattenedPoints(): ColorPoint[];
159 | get colors(): [number, number, number][];
160 | cssColors(mode?: "hsl" | "oklch" | "lch"): string[];
161 | get colorsCSS(): string[];
162 | get colorsCSSlch(): string[];
163 | get colorsCSSoklch(): string[];
164 | shiftHue(hShift?: number): void;
165 | /**
166 | * Returns a color at a specific position along the entire color line (0-1)
167 | * Treats all segments as one continuous path, respecting easing functions
168 | * @param t Position along the line (0-1), where 0 is start and 1 is end
169 | * @returns ColorPoint at the specified position
170 | * @example
171 | * getColorAt(0) // Returns color at the very beginning
172 | * getColorAt(0.5) // Returns color at the middle of the entire journey
173 | * getColorAt(1) // Returns color at the very end
174 | */
175 | getColorAt(t: number): ColorPoint;
176 | /**
177 | * Determines whether easing should be inverted for a given segment
178 | * @param segmentIndex The index of the segment
179 | * @returns Whether easing should be inverted
180 | */
181 | private shouldInvertEaseForSegment;
182 | }
183 |
--------------------------------------------------------------------------------
/dist/index.min.mjs:
--------------------------------------------------------------------------------
1 | var m=(i,t)=>{let[o,n,e]=i,c=.5,h=.5,r=Math.atan2(n-h,o-c)*(180/Math.PI);r=(360+r)%360;let l=e,u=Math.sqrt(Math.pow(n-h,2)+Math.pow(o-c,2))/c;return[r,l,t?1-u:u]},f=(i,t)=>{let[o,n,e]=i,c=.5,h=.5,s=o/(180/Math.PI),r=(t?1-e:e)*c,l=c+r*Math.cos(s),a=h+r*Math.sin(s);return[l,a,n]},F=(i=Math.random()*360,t=[Math.random(),Math.random()],o=[.75+Math.random()*.2,.3+Math.random()*.2])=>[[i,t[0],o[0]],[(i+60+Math.random()*180)%360,t[1],o[1]]],_=(i,t)=>{let e=i-.5,c=t-.5,h=Math.hypot(e,c);return h<=.5?[i,t]:[.5+e/h*.5,.5+c/h*.5]},X=(i=Math.random()*360,t=[Math.random(),Math.random(),Math.random()],o=[.75+Math.random()*.2,Math.random()*.2,.75+Math.random()*.2])=>[[i,t[0],o[0]],[(i+60+Math.random()*180)%360,t[1],o[1]],[(i+60+Math.random()*180)%360,t[2],o[2]]],v=(i,t,o,n=!1,e=(s,r)=>r?1-s:s,c=(s,r)=>r?1-s:s,h=(s,r)=>r?1-s:s)=>{let s=e(i,n),r=c(i,n),l=h(i,n),a=(1-s)*t[0]+s*o[0],u=(1-r)*t[1]+r*o[1],P=(1-l)*t[2]+l*o[2];return[a,u,P]},x=(i,t,o=4,n=!1,e=(s,r)=>r?1-s:s,c=(s,r)=>r?1-s:s,h=(s,r)=>r?1-s:s)=>{let s=[];for(let r=0;ri,L=(i,t=!1)=>t?1-(1-i)**2:i**2,A=(i,t=!1)=>t?1-(1-i)**3:i**3,y=(i,t=!1)=>t?1-(1-i)**4:i**4,w=(i,t=!1)=>t?1-(1-i)**5:i**5,p=(i,t=!1)=>t?1-Math.sin((1-i)*Math.PI/2):Math.sin(i*Math.PI/2),S=(i,t=!1)=>t?1-Math.asin(1-i)/(Math.PI/2):Math.asin(i)/(Math.PI/2),E=(i,t=!1)=>t?1-Math.sqrt(1-i**2):1-Math.sqrt(1-i),T=i=>i**2*(3-2*i),I={linearPosition:V,exponentialPosition:L,quadraticPosition:A,cubicPosition:y,quarticPosition:w,sinusoidalPosition:p,asinusoidalPosition:S,arcPosition:E,smoothStepPosition:T},C=(i,t,o=!1)=>{let n=i[0],e=t[0],c=0;o&&n!==null&&e!==null?(c=Math.min(Math.abs(n-e),360-Math.abs(n-e)),c=c/360):c=n===null||e===null?0:n-e;let h=c,s=i[1]===null||t[1]===null?0:t[1]-i[1],r=i[2]===null||t[2]===null?0:t[2]-i[2];return Math.sqrt(h*h+s*s+r*r)},d=class{constructor({xyz:t,color:o,invertedLightness:n=!1}={}){this.x=0;this.y=0;this.z=0;this.color=[0,0,0];this._invertedLightness=!1;this._invertedLightness=n,this.positionOrColor({xyz:t,color:o,invertedLightness:n})}positionOrColor({xyz:t,color:o,invertedLightness:n=!1}){if(this._invertedLightness=n,t&&o||!t&&!o)throw new Error("Point must be initialized with either x,y,z or hsl");t?(this.x=t[0],this.y=t[1],this.z=t[2],this.color=m([this.x,this.y,this.z],n)):o&&(this.color=o,[this.x,this.y,this.z]=f(o,n))}set position([t,o,n]){this.x=t,this.y=o,this.z=n,this.color=m([this.x,this.y,this.z],this._invertedLightness)}get position(){return[this.x,this.y,this.z]}set hsl([t,o,n]){this.color=[t,o,n],[this.x,this.y,this.z]=f(this.color,this._invertedLightness)}get hsl(){return this.color}get hslCSS(){let[t,o,n]=this.color;return`hsl(${t.toFixed(2)}, ${(o*100).toFixed(2)}%, ${(n*100).toFixed(2)}%)`}get oklchCSS(){let[t,o,n]=this.color;return`oklch(${(n*100).toFixed(2)}% ${(o*.4).toFixed(3)} ${t.toFixed(2)})`}get lchCSS(){let[t,o,n]=this.color;return`lch(${(n*100).toFixed(2)}% ${(o*150).toFixed(2)} ${t.toFixed(2)})`}set invertedLightness(t){this._invertedLightness=t,this.color=m([this.x,this.y,this.z],this._invertedLightness)}get invertedLightness(){return this._invertedLightness}shiftHue(t){this.color[0]=(360+(this.color[0]+t))%360,[this.x,this.y,this.z]=f(this.color,this._invertedLightness)}},g=class{constructor({anchorColors:t=F(),numPoints:o=4,positionFunction:n=p,positionFunctionX:e,positionFunctionY:c,positionFunctionZ:h,closedLoop:s,invertedLightness:r,clampToCircle:l}={anchorColors:F(),numPoints:4,positionFunction:p,closedLoop:!1}){this._positionFunctionX=p;this._positionFunctionY=p;this._positionFunctionZ=p;this.connectLastAndFirstAnchor=!1;this._animationFrame=null;this._invertedLightness=!1;this._clampToCircle=!1;if(!t||t.length<2)throw new Error("Must have at least two anchor colors");this._anchorPoints=t.map(a=>new d({color:a,invertedLightness:r})),this._numPoints=o+2,this._positionFunctionX=e||n||p,this._positionFunctionY=c||n||p,this._positionFunctionZ=h||n||p,this.connectLastAndFirstAnchor=s||!1,this._invertedLightness=r||!1,this._clampToCircle=l||!1,this.updateAnchorPairs()}get numPoints(){return this._numPoints-2}set numPoints(t){if(t<1)throw new Error("Must have at least one point");this._numPoints=t+2,this.updateAnchorPairs()}set positionFunction(t){if(Array.isArray(t)){if(t.length!==3)throw new Error("Position function array must have 3 elements");if(typeof t[0]!="function"||typeof t[1]!="function"||typeof t[2]!="function")throw new Error("Position function array must have 3 functions");this._positionFunctionX=t[0],this._positionFunctionY=t[1],this._positionFunctionZ=t[2]}else this._positionFunctionX=t,this._positionFunctionY=t,this._positionFunctionZ=t;this.updateAnchorPairs()}get positionFunction(){return this._positionFunctionX===this._positionFunctionY&&this._positionFunctionX===this._positionFunctionZ?this._positionFunctionX:[this._positionFunctionX,this._positionFunctionY,this._positionFunctionZ]}set positionFunctionX(t){this._positionFunctionX=t,this.updateAnchorPairs()}get positionFunctionX(){return this._positionFunctionX}set positionFunctionY(t){this._positionFunctionY=t,this.updateAnchorPairs()}get positionFunctionY(){return this._positionFunctionY}set positionFunctionZ(t){this._positionFunctionZ=t,this.updateAnchorPairs()}get positionFunctionZ(){return this._positionFunctionZ}get clampToCircle(){return this._clampToCircle}set clampToCircle(t){this._clampToCircle=t}get anchorPoints(){return this._anchorPoints}set anchorPoints(t){this._anchorPoints=t,this.updateAnchorPairs()}updateAnchorPairs(){this._anchorPairs=[];let t=this.connectLastAndFirstAnchor?this.anchorPoints.length:this.anchorPoints.length-1;for(let o=0;o{let e=o[0]?o[0].position:[0,0,0],c=o[1]?o[1].position:[0,0,0],h=this.shouldInvertEaseForSegment(n);return x(e,c,this._numPoints,!!h,this.positionFunctionX,this.positionFunctionY,this.positionFunctionZ).map(s=>new d({xyz:s,invertedLightness:this._invertedLightness}))})}addAnchorPoint({xyz:t,color:o,insertAtIndex:n,clamp:e}){let c=t;if((e??this._clampToCircle)&&t){let[r,l,a]=t,[u,P]=_(r,l);c=[u,P,a]}let s=new d({xyz:c,color:o,invertedLightness:this._invertedLightness});return n!==void 0?this.anchorPoints.splice(n,0,s):this.anchorPoints.push(s),this.updateAnchorPairs(),s}removeAnchorPoint({point:t,index:o}){if(!t&&o===void 0)throw new Error("Must provide a point or index");if(this.anchorPoints.length<3)throw new Error("Must have at least two anchor points");let n;if(o!==void 0?n=o:t&&(n=this.anchorPoints.indexOf(t)),n>-1&&nC(s.position,t)):o&&(e=this.anchorPoints.map(s=>C(s.hsl,o,!0)));let c=Math.min(...e);if(c>n)return null;let h=e.indexOf(c);return this.anchorPoints[h]||null}set closedLoop(t){this.connectLastAndFirstAnchor=t,this.updateAnchorPairs()}get closedLoop(){return this.connectLastAndFirstAnchor}set invertedLightness(t){this._invertedLightness=t,this.anchorPoints.forEach(o=>o.invertedLightness=t),this.updateAnchorPairs()}get invertedLightness(){return this._invertedLightness}get flattenedPoints(){return this.points.flat().filter((t,o)=>o!=0?o%this._numPoints:!0)}get colors(){let t=this.flattenedPoints.map(o=>o.color);return this.connectLastAndFirstAnchor&&this._anchorPoints.length!==2&&t.pop(),t}cssColors(t="hsl"){let o={hsl:e=>e.hslCSS,oklch:e=>e.oklchCSS,lch:e=>e.lchCSS},n=this.flattenedPoints.map(o[t]);return this.connectLastAndFirstAnchor&&n.pop(),n}get colorsCSS(){return this.cssColors("hsl")}get colorsCSSlch(){return this.cssColors("lch")}get colorsCSSoklch(){return this.cssColors("oklch")}shiftHue(t=20){this.anchorPoints.forEach(o=>o.shiftHue(t)),this.updateAnchorPairs()}getColorAt(t){if(t<0||t>1)throw new Error("Position must be between 0 and 1");if(this.anchorPoints.length===0)throw new Error("No anchor points available");let o=this.connectLastAndFirstAnchor?this.anchorPoints.length:this.anchorPoints.length-1,n=this.connectLastAndFirstAnchor&&this.anchorPoints.length===2?2:o,e=t*n,c=Math.floor(e),h=e-c,s=c>=n?n-1:c,r=c>=n?1:h,l=this._anchorPairs[s];if(!l||l.length<2||!l[0]||!l[1])return new d({color:this.anchorPoints[0]?.color||[0,0,0],invertedLightness:this._invertedLightness});let a=l[0].position,u=l[1].position,P=this.shouldInvertEaseForSegment(s),M=v(r,a,u,P,this._positionFunctionX,this._positionFunctionY,this._positionFunctionZ);return new d({xyz:M,invertedLightness:this._invertedLightness})}shouldInvertEaseForSegment(t){return!!(t%2||this.connectLastAndFirstAnchor&&this.anchorPoints.length===2&&t===0)}},{p5:b}=globalThis;if(b&&b.VERSION&&b.VERSION.startsWith("1.")){console.info("p5 < 1.x detected, adding poline to p5 prototype");let i=new g;b.prototype.poline=i;let t=()=>i.colors.map(o=>`hsl(${Math.round(o[0])},${o[1]*100}%,${o[2]*100}%)`);b.prototype.registerMethod("polineColors",t),globalThis.poline=i,globalThis.polineColors=t}export{d as ColorPoint,g as Poline,_ as clampToCircle,f as hslToPoint,m as pointToHSL,I as positionFunctions,F as randomHSLPair,X as randomHSLTriple};
2 |
--------------------------------------------------------------------------------
/dist/index.min.cjs:
--------------------------------------------------------------------------------
1 | "use strict";var F=Object.defineProperty;var L=Object.getOwnPropertyDescriptor;var A=Object.getOwnPropertyNames;var y=Object.prototype.hasOwnProperty;var w=(i,t)=>{for(var o in t)F(i,o,{get:t[o],enumerable:!0})},S=(i,t,o,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let s of A(t))!y.call(i,s)&&s!==o&&F(i,s,{get:()=>t[s],enumerable:!(n=L(t,s))||n.enumerable});return i};var E=i=>S(F({},"__esModule",{value:!0}),i);var H={};w(H,{ColorPoint:()=>d,Poline:()=>g,clampToCircle:()=>C,hslToPoint:()=>f,pointToHSL:()=>m,positionFunctions:()=>N,randomHSLPair:()=>_,randomHSLTriple:()=>T});module.exports=E(H);var m=(i,t)=>{let[o,n,s]=i,r=.5,h=.5,c=Math.atan2(n-h,o-r)*(180/Math.PI);c=(360+c)%360;let l=s,u=Math.sqrt(Math.pow(n-h,2)+Math.pow(o-r,2))/r;return[c,l,t?1-u:u]},f=(i,t)=>{let[o,n,s]=i,r=.5,h=.5,e=o/(180/Math.PI),c=(t?1-s:s)*r,l=r+c*Math.cos(e),a=h+c*Math.sin(e);return[l,a,n]},_=(i=Math.random()*360,t=[Math.random(),Math.random()],o=[.75+Math.random()*.2,.3+Math.random()*.2])=>[[i,t[0],o[0]],[(i+60+Math.random()*180)%360,t[1],o[1]]],C=(i,t)=>{let s=i-.5,r=t-.5,h=Math.hypot(s,r);return h<=.5?[i,t]:[.5+s/h*.5,.5+r/h*.5]},T=(i=Math.random()*360,t=[Math.random(),Math.random(),Math.random()],o=[.75+Math.random()*.2,Math.random()*.2,.75+Math.random()*.2])=>[[i,t[0],o[0]],[(i+60+Math.random()*180)%360,t[1],o[1]],[(i+60+Math.random()*180)%360,t[2],o[2]]],x=(i,t,o,n=!1,s=(e,c)=>c?1-e:e,r=(e,c)=>c?1-e:e,h=(e,c)=>c?1-e:e)=>{let e=s(i,n),c=r(i,n),l=h(i,n),a=(1-e)*t[0]+e*o[0],u=(1-c)*t[1]+c*o[1],P=(1-l)*t[2]+l*o[2];return[a,u,P]},X=(i,t,o=4,n=!1,s=(e,c)=>c?1-e:e,r=(e,c)=>c?1-e:e,h=(e,c)=>c?1-e:e)=>{let e=[];for(let c=0;ci,z=(i,t=!1)=>t?1-(1-i)**2:i**2,Y=(i,t=!1)=>t?1-(1-i)**3:i**3,Z=(i,t=!1)=>t?1-(1-i)**4:i**4,$=(i,t=!1)=>t?1-(1-i)**5:i**5,p=(i,t=!1)=>t?1-Math.sin((1-i)*Math.PI/2):Math.sin(i*Math.PI/2),O=(i,t=!1)=>t?1-Math.asin(1-i)/(Math.PI/2):Math.asin(i)/(Math.PI/2),k=(i,t=!1)=>t?1-Math.sqrt(1-i**2):1-Math.sqrt(1-i),q=i=>i**2*(3-2*i),N={linearPosition:I,exponentialPosition:z,quadraticPosition:Y,cubicPosition:Z,quarticPosition:$,sinusoidalPosition:p,asinusoidalPosition:O,arcPosition:k,smoothStepPosition:q},M=(i,t,o=!1)=>{let n=i[0],s=t[0],r=0;o&&n!==null&&s!==null?(r=Math.min(Math.abs(n-s),360-Math.abs(n-s)),r=r/360):r=n===null||s===null?0:n-s;let h=r,e=i[1]===null||t[1]===null?0:t[1]-i[1],c=i[2]===null||t[2]===null?0:t[2]-i[2];return Math.sqrt(h*h+e*e+c*c)},d=class{constructor({xyz:t,color:o,invertedLightness:n=!1}={}){this.x=0;this.y=0;this.z=0;this.color=[0,0,0];this._invertedLightness=!1;this._invertedLightness=n,this.positionOrColor({xyz:t,color:o,invertedLightness:n})}positionOrColor({xyz:t,color:o,invertedLightness:n=!1}){if(this._invertedLightness=n,t&&o||!t&&!o)throw new Error("Point must be initialized with either x,y,z or hsl");t?(this.x=t[0],this.y=t[1],this.z=t[2],this.color=m([this.x,this.y,this.z],n)):o&&(this.color=o,[this.x,this.y,this.z]=f(o,n))}set position([t,o,n]){this.x=t,this.y=o,this.z=n,this.color=m([this.x,this.y,this.z],this._invertedLightness)}get position(){return[this.x,this.y,this.z]}set hsl([t,o,n]){this.color=[t,o,n],[this.x,this.y,this.z]=f(this.color,this._invertedLightness)}get hsl(){return this.color}get hslCSS(){let[t,o,n]=this.color;return`hsl(${t.toFixed(2)}, ${(o*100).toFixed(2)}%, ${(n*100).toFixed(2)}%)`}get oklchCSS(){let[t,o,n]=this.color;return`oklch(${(n*100).toFixed(2)}% ${(o*.4).toFixed(3)} ${t.toFixed(2)})`}get lchCSS(){let[t,o,n]=this.color;return`lch(${(n*100).toFixed(2)}% ${(o*150).toFixed(2)} ${t.toFixed(2)})`}set invertedLightness(t){this._invertedLightness=t,this.color=m([this.x,this.y,this.z],this._invertedLightness)}get invertedLightness(){return this._invertedLightness}shiftHue(t){this.color[0]=(360+(this.color[0]+t))%360,[this.x,this.y,this.z]=f(this.color,this._invertedLightness)}},g=class{constructor({anchorColors:t=_(),numPoints:o=4,positionFunction:n=p,positionFunctionX:s,positionFunctionY:r,positionFunctionZ:h,closedLoop:e,invertedLightness:c,clampToCircle:l}={anchorColors:_(),numPoints:4,positionFunction:p,closedLoop:!1}){this._positionFunctionX=p;this._positionFunctionY=p;this._positionFunctionZ=p;this.connectLastAndFirstAnchor=!1;this._animationFrame=null;this._invertedLightness=!1;this._clampToCircle=!1;if(!t||t.length<2)throw new Error("Must have at least two anchor colors");this._anchorPoints=t.map(a=>new d({color:a,invertedLightness:c})),this._numPoints=o+2,this._positionFunctionX=s||n||p,this._positionFunctionY=r||n||p,this._positionFunctionZ=h||n||p,this.connectLastAndFirstAnchor=e||!1,this._invertedLightness=c||!1,this._clampToCircle=l||!1,this.updateAnchorPairs()}get numPoints(){return this._numPoints-2}set numPoints(t){if(t<1)throw new Error("Must have at least one point");this._numPoints=t+2,this.updateAnchorPairs()}set positionFunction(t){if(Array.isArray(t)){if(t.length!==3)throw new Error("Position function array must have 3 elements");if(typeof t[0]!="function"||typeof t[1]!="function"||typeof t[2]!="function")throw new Error("Position function array must have 3 functions");this._positionFunctionX=t[0],this._positionFunctionY=t[1],this._positionFunctionZ=t[2]}else this._positionFunctionX=t,this._positionFunctionY=t,this._positionFunctionZ=t;this.updateAnchorPairs()}get positionFunction(){return this._positionFunctionX===this._positionFunctionY&&this._positionFunctionX===this._positionFunctionZ?this._positionFunctionX:[this._positionFunctionX,this._positionFunctionY,this._positionFunctionZ]}set positionFunctionX(t){this._positionFunctionX=t,this.updateAnchorPairs()}get positionFunctionX(){return this._positionFunctionX}set positionFunctionY(t){this._positionFunctionY=t,this.updateAnchorPairs()}get positionFunctionY(){return this._positionFunctionY}set positionFunctionZ(t){this._positionFunctionZ=t,this.updateAnchorPairs()}get positionFunctionZ(){return this._positionFunctionZ}get clampToCircle(){return this._clampToCircle}set clampToCircle(t){this._clampToCircle=t}get anchorPoints(){return this._anchorPoints}set anchorPoints(t){this._anchorPoints=t,this.updateAnchorPairs()}updateAnchorPairs(){this._anchorPairs=[];let t=this.connectLastAndFirstAnchor?this.anchorPoints.length:this.anchorPoints.length-1;for(let o=0;o{let s=o[0]?o[0].position:[0,0,0],r=o[1]?o[1].position:[0,0,0],h=this.shouldInvertEaseForSegment(n);return X(s,r,this._numPoints,!!h,this.positionFunctionX,this.positionFunctionY,this.positionFunctionZ).map(e=>new d({xyz:e,invertedLightness:this._invertedLightness}))})}addAnchorPoint({xyz:t,color:o,insertAtIndex:n,clamp:s}){let r=t;if((s!=null?s:this._clampToCircle)&&t){let[c,l,a]=t,[u,P]=C(c,l);r=[u,P,a]}let e=new d({xyz:r,color:o,invertedLightness:this._invertedLightness});return n!==void 0?this.anchorPoints.splice(n,0,e):this.anchorPoints.push(e),this.updateAnchorPairs(),e}removeAnchorPoint({point:t,index:o}){if(!t&&o===void 0)throw new Error("Must provide a point or index");if(this.anchorPoints.length<3)throw new Error("Must have at least two anchor points");let n;if(o!==void 0?n=o:t&&(n=this.anchorPoints.indexOf(t)),n>-1&&nM(e.position,t)):o&&(s=this.anchorPoints.map(e=>M(e.hsl,o,!0)));let r=Math.min(...s);if(r>n)return null;let h=s.indexOf(r);return this.anchorPoints[h]||null}set closedLoop(t){this.connectLastAndFirstAnchor=t,this.updateAnchorPairs()}get closedLoop(){return this.connectLastAndFirstAnchor}set invertedLightness(t){this._invertedLightness=t,this.anchorPoints.forEach(o=>o.invertedLightness=t),this.updateAnchorPairs()}get invertedLightness(){return this._invertedLightness}get flattenedPoints(){return this.points.flat().filter((t,o)=>o!=0?o%this._numPoints:!0)}get colors(){let t=this.flattenedPoints.map(o=>o.color);return this.connectLastAndFirstAnchor&&this._anchorPoints.length!==2&&t.pop(),t}cssColors(t="hsl"){let o={hsl:s=>s.hslCSS,oklch:s=>s.oklchCSS,lch:s=>s.lchCSS},n=this.flattenedPoints.map(o[t]);return this.connectLastAndFirstAnchor&&n.pop(),n}get colorsCSS(){return this.cssColors("hsl")}get colorsCSSlch(){return this.cssColors("lch")}get colorsCSSoklch(){return this.cssColors("oklch")}shiftHue(t=20){this.anchorPoints.forEach(o=>o.shiftHue(t)),this.updateAnchorPairs()}getColorAt(t){var v;if(t<0||t>1)throw new Error("Position must be between 0 and 1");if(this.anchorPoints.length===0)throw new Error("No anchor points available");let o=this.connectLastAndFirstAnchor?this.anchorPoints.length:this.anchorPoints.length-1,n=this.connectLastAndFirstAnchor&&this.anchorPoints.length===2?2:o,s=t*n,r=Math.floor(s),h=s-r,e=r>=n?n-1:r,c=r>=n?1:h,l=this._anchorPairs[e];if(!l||l.length<2||!l[0]||!l[1])return new d({color:((v=this.anchorPoints[0])==null?void 0:v.color)||[0,0,0],invertedLightness:this._invertedLightness});let a=l[0].position,u=l[1].position,P=this.shouldInvertEaseForSegment(e),V=x(c,a,u,P,this._positionFunctionX,this._positionFunctionY,this._positionFunctionZ);return new d({xyz:V,invertedLightness:this._invertedLightness})}shouldInvertEaseForSegment(t){return!!(t%2||this.connectLastAndFirstAnchor&&this.anchorPoints.length===2&&t===0)}},{p5:b}=globalThis;if(b&&b.VERSION&&b.VERSION.startsWith("1.")){console.info("p5 < 1.x detected, adding poline to p5 prototype");let i=new g;b.prototype.poline=i;let t=()=>i.colors.map(o=>`hsl(${Math.round(o[0])},${o[1]*100}%,${o[2]*100}%)`);b.prototype.registerMethod("polineColors",t),globalThis.poline=i,globalThis.polineColors=t}
2 |
--------------------------------------------------------------------------------
/dist/index.min.js:
--------------------------------------------------------------------------------
1 | "use strict";var poline=(()=>{var F=Object.defineProperty;var L=Object.getOwnPropertyDescriptor;var A=Object.getOwnPropertyNames;var y=Object.prototype.hasOwnProperty;var w=(i,t)=>{for(var o in t)F(i,o,{get:t[o],enumerable:!0})},S=(i,t,o,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let s of A(t))!y.call(i,s)&&s!==o&&F(i,s,{get:()=>t[s],enumerable:!(n=L(t,s))||n.enumerable});return i};var E=i=>S(F({},"__esModule",{value:!0}),i);var H={};w(H,{ColorPoint:()=>d,Poline:()=>g,clampToCircle:()=>C,hslToPoint:()=>f,pointToHSL:()=>m,positionFunctions:()=>N,randomHSLPair:()=>_,randomHSLTriple:()=>T});var m=(i,t)=>{let[o,n,s]=i,r=.5,h=.5,c=Math.atan2(n-h,o-r)*(180/Math.PI);c=(360+c)%360;let l=s,u=Math.sqrt(Math.pow(n-h,2)+Math.pow(o-r,2))/r;return[c,l,t?1-u:u]},f=(i,t)=>{let[o,n,s]=i,r=.5,h=.5,e=o/(180/Math.PI),c=(t?1-s:s)*r,l=r+c*Math.cos(e),a=h+c*Math.sin(e);return[l,a,n]},_=(i=Math.random()*360,t=[Math.random(),Math.random()],o=[.75+Math.random()*.2,.3+Math.random()*.2])=>[[i,t[0],o[0]],[(i+60+Math.random()*180)%360,t[1],o[1]]],C=(i,t)=>{let s=i-.5,r=t-.5,h=Math.hypot(s,r);return h<=.5?[i,t]:[.5+s/h*.5,.5+r/h*.5]},T=(i=Math.random()*360,t=[Math.random(),Math.random(),Math.random()],o=[.75+Math.random()*.2,Math.random()*.2,.75+Math.random()*.2])=>[[i,t[0],o[0]],[(i+60+Math.random()*180)%360,t[1],o[1]],[(i+60+Math.random()*180)%360,t[2],o[2]]],x=(i,t,o,n=!1,s=(e,c)=>c?1-e:e,r=(e,c)=>c?1-e:e,h=(e,c)=>c?1-e:e)=>{let e=s(i,n),c=r(i,n),l=h(i,n),a=(1-e)*t[0]+e*o[0],u=(1-c)*t[1]+c*o[1],P=(1-l)*t[2]+l*o[2];return[a,u,P]},X=(i,t,o=4,n=!1,s=(e,c)=>c?1-e:e,r=(e,c)=>c?1-e:e,h=(e,c)=>c?1-e:e)=>{let e=[];for(let c=0;ci,z=(i,t=!1)=>t?1-(1-i)**2:i**2,Y=(i,t=!1)=>t?1-(1-i)**3:i**3,Z=(i,t=!1)=>t?1-(1-i)**4:i**4,$=(i,t=!1)=>t?1-(1-i)**5:i**5,p=(i,t=!1)=>t?1-Math.sin((1-i)*Math.PI/2):Math.sin(i*Math.PI/2),O=(i,t=!1)=>t?1-Math.asin(1-i)/(Math.PI/2):Math.asin(i)/(Math.PI/2),k=(i,t=!1)=>t?1-Math.sqrt(1-i**2):1-Math.sqrt(1-i),q=i=>i**2*(3-2*i),N={linearPosition:I,exponentialPosition:z,quadraticPosition:Y,cubicPosition:Z,quarticPosition:$,sinusoidalPosition:p,asinusoidalPosition:O,arcPosition:k,smoothStepPosition:q},M=(i,t,o=!1)=>{let n=i[0],s=t[0],r=0;o&&n!==null&&s!==null?(r=Math.min(Math.abs(n-s),360-Math.abs(n-s)),r=r/360):r=n===null||s===null?0:n-s;let h=r,e=i[1]===null||t[1]===null?0:t[1]-i[1],c=i[2]===null||t[2]===null?0:t[2]-i[2];return Math.sqrt(h*h+e*e+c*c)},d=class{constructor({xyz:t,color:o,invertedLightness:n=!1}={}){this.x=0;this.y=0;this.z=0;this.color=[0,0,0];this._invertedLightness=!1;this._invertedLightness=n,this.positionOrColor({xyz:t,color:o,invertedLightness:n})}positionOrColor({xyz:t,color:o,invertedLightness:n=!1}){if(this._invertedLightness=n,t&&o||!t&&!o)throw new Error("Point must be initialized with either x,y,z or hsl");t?(this.x=t[0],this.y=t[1],this.z=t[2],this.color=m([this.x,this.y,this.z],n)):o&&(this.color=o,[this.x,this.y,this.z]=f(o,n))}set position([t,o,n]){this.x=t,this.y=o,this.z=n,this.color=m([this.x,this.y,this.z],this._invertedLightness)}get position(){return[this.x,this.y,this.z]}set hsl([t,o,n]){this.color=[t,o,n],[this.x,this.y,this.z]=f(this.color,this._invertedLightness)}get hsl(){return this.color}get hslCSS(){let[t,o,n]=this.color;return`hsl(${t.toFixed(2)}, ${(o*100).toFixed(2)}%, ${(n*100).toFixed(2)}%)`}get oklchCSS(){let[t,o,n]=this.color;return`oklch(${(n*100).toFixed(2)}% ${(o*.4).toFixed(3)} ${t.toFixed(2)})`}get lchCSS(){let[t,o,n]=this.color;return`lch(${(n*100).toFixed(2)}% ${(o*150).toFixed(2)} ${t.toFixed(2)})`}set invertedLightness(t){this._invertedLightness=t,this.color=m([this.x,this.y,this.z],this._invertedLightness)}get invertedLightness(){return this._invertedLightness}shiftHue(t){this.color[0]=(360+(this.color[0]+t))%360,[this.x,this.y,this.z]=f(this.color,this._invertedLightness)}},g=class{constructor({anchorColors:t=_(),numPoints:o=4,positionFunction:n=p,positionFunctionX:s,positionFunctionY:r,positionFunctionZ:h,closedLoop:e,invertedLightness:c,clampToCircle:l}={anchorColors:_(),numPoints:4,positionFunction:p,closedLoop:!1}){this._positionFunctionX=p;this._positionFunctionY=p;this._positionFunctionZ=p;this.connectLastAndFirstAnchor=!1;this._animationFrame=null;this._invertedLightness=!1;this._clampToCircle=!1;if(!t||t.length<2)throw new Error("Must have at least two anchor colors");this._anchorPoints=t.map(a=>new d({color:a,invertedLightness:c})),this._numPoints=o+2,this._positionFunctionX=s||n||p,this._positionFunctionY=r||n||p,this._positionFunctionZ=h||n||p,this.connectLastAndFirstAnchor=e||!1,this._invertedLightness=c||!1,this._clampToCircle=l||!1,this.updateAnchorPairs()}get numPoints(){return this._numPoints-2}set numPoints(t){if(t<1)throw new Error("Must have at least one point");this._numPoints=t+2,this.updateAnchorPairs()}set positionFunction(t){if(Array.isArray(t)){if(t.length!==3)throw new Error("Position function array must have 3 elements");if(typeof t[0]!="function"||typeof t[1]!="function"||typeof t[2]!="function")throw new Error("Position function array must have 3 functions");this._positionFunctionX=t[0],this._positionFunctionY=t[1],this._positionFunctionZ=t[2]}else this._positionFunctionX=t,this._positionFunctionY=t,this._positionFunctionZ=t;this.updateAnchorPairs()}get positionFunction(){return this._positionFunctionX===this._positionFunctionY&&this._positionFunctionX===this._positionFunctionZ?this._positionFunctionX:[this._positionFunctionX,this._positionFunctionY,this._positionFunctionZ]}set positionFunctionX(t){this._positionFunctionX=t,this.updateAnchorPairs()}get positionFunctionX(){return this._positionFunctionX}set positionFunctionY(t){this._positionFunctionY=t,this.updateAnchorPairs()}get positionFunctionY(){return this._positionFunctionY}set positionFunctionZ(t){this._positionFunctionZ=t,this.updateAnchorPairs()}get positionFunctionZ(){return this._positionFunctionZ}get clampToCircle(){return this._clampToCircle}set clampToCircle(t){this._clampToCircle=t}get anchorPoints(){return this._anchorPoints}set anchorPoints(t){this._anchorPoints=t,this.updateAnchorPairs()}updateAnchorPairs(){this._anchorPairs=[];let t=this.connectLastAndFirstAnchor?this.anchorPoints.length:this.anchorPoints.length-1;for(let o=0;o{let s=o[0]?o[0].position:[0,0,0],r=o[1]?o[1].position:[0,0,0],h=this.shouldInvertEaseForSegment(n);return X(s,r,this._numPoints,!!h,this.positionFunctionX,this.positionFunctionY,this.positionFunctionZ).map(e=>new d({xyz:e,invertedLightness:this._invertedLightness}))})}addAnchorPoint({xyz:t,color:o,insertAtIndex:n,clamp:s}){let r=t;if((s!=null?s:this._clampToCircle)&&t){let[c,l,a]=t,[u,P]=C(c,l);r=[u,P,a]}let e=new d({xyz:r,color:o,invertedLightness:this._invertedLightness});return n!==void 0?this.anchorPoints.splice(n,0,e):this.anchorPoints.push(e),this.updateAnchorPairs(),e}removeAnchorPoint({point:t,index:o}){if(!t&&o===void 0)throw new Error("Must provide a point or index");if(this.anchorPoints.length<3)throw new Error("Must have at least two anchor points");let n;if(o!==void 0?n=o:t&&(n=this.anchorPoints.indexOf(t)),n>-1&&nM(e.position,t)):o&&(s=this.anchorPoints.map(e=>M(e.hsl,o,!0)));let r=Math.min(...s);if(r>n)return null;let h=s.indexOf(r);return this.anchorPoints[h]||null}set closedLoop(t){this.connectLastAndFirstAnchor=t,this.updateAnchorPairs()}get closedLoop(){return this.connectLastAndFirstAnchor}set invertedLightness(t){this._invertedLightness=t,this.anchorPoints.forEach(o=>o.invertedLightness=t),this.updateAnchorPairs()}get invertedLightness(){return this._invertedLightness}get flattenedPoints(){return this.points.flat().filter((t,o)=>o!=0?o%this._numPoints:!0)}get colors(){let t=this.flattenedPoints.map(o=>o.color);return this.connectLastAndFirstAnchor&&this._anchorPoints.length!==2&&t.pop(),t}cssColors(t="hsl"){let o={hsl:s=>s.hslCSS,oklch:s=>s.oklchCSS,lch:s=>s.lchCSS},n=this.flattenedPoints.map(o[t]);return this.connectLastAndFirstAnchor&&n.pop(),n}get colorsCSS(){return this.cssColors("hsl")}get colorsCSSlch(){return this.cssColors("lch")}get colorsCSSoklch(){return this.cssColors("oklch")}shiftHue(t=20){this.anchorPoints.forEach(o=>o.shiftHue(t)),this.updateAnchorPairs()}getColorAt(t){var v;if(t<0||t>1)throw new Error("Position must be between 0 and 1");if(this.anchorPoints.length===0)throw new Error("No anchor points available");let o=this.connectLastAndFirstAnchor?this.anchorPoints.length:this.anchorPoints.length-1,n=this.connectLastAndFirstAnchor&&this.anchorPoints.length===2?2:o,s=t*n,r=Math.floor(s),h=s-r,e=r>=n?n-1:r,c=r>=n?1:h,l=this._anchorPairs[e];if(!l||l.length<2||!l[0]||!l[1])return new d({color:((v=this.anchorPoints[0])==null?void 0:v.color)||[0,0,0],invertedLightness:this._invertedLightness});let a=l[0].position,u=l[1].position,P=this.shouldInvertEaseForSegment(e),V=x(c,a,u,P,this._positionFunctionX,this._positionFunctionY,this._positionFunctionZ);return new d({xyz:V,invertedLightness:this._invertedLightness})}shouldInvertEaseForSegment(t){return!!(t%2||this.connectLastAndFirstAnchor&&this.anchorPoints.length===2&&t===0)}},{p5:b}=globalThis;if(b&&b.VERSION&&b.VERSION.startsWith("1.")){console.info("p5 < 1.x detected, adding poline to p5 prototype");let i=new g;b.prototype.poline=i;let t=()=>i.colors.map(o=>`hsl(${Math.round(o[0])},${o[1]*100}%,${o[2]*100}%)`);b.prototype.registerMethod("polineColors",t),globalThis.poline=i,globalThis.polineColors=t}return E(H);})();
2 |
--------------------------------------------------------------------------------
/dist/picker.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Poline Picker Example
7 |
8 |
9 |
11 |
195 |
196 |
197 |
198 |
Poline Picker
199 |
200 | Experience the magic of "poline" for yourself, dear color explorer.
201 | Drag existing anchor points to adjust colors.
202 | Drag the outer ring of an anchor point to adjust its saturation.
203 | Press P to add a new point at cursor position,
204 | and press ⌫ to remove the last selected anchor point.
205 | Try using the ← and → keys to change the hue of all colors.
206 | Hold Shift while adjusting saturation for finer control.
207 |