+794
| "use strict"; | ||
| var __defProp = Object.defineProperty; | ||
| var __getOwnPropDesc = Object.getOwnPropertyDescriptor; | ||
| var __getOwnPropNames = Object.getOwnPropertyNames; | ||
| var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
| var __pow = Math.pow; | ||
| var __export = (target, all) => { | ||
| for (var name in all) | ||
| __defProp(target, name, { get: all[name], enumerable: true }); | ||
| }; | ||
| var __copyProps = (to, from, except, desc) => { | ||
| if (from && typeof from === "object" || typeof from === "function") { | ||
| for (let key of __getOwnPropNames(from)) | ||
| if (!__hasOwnProp.call(to, key) && key !== except) | ||
| __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); | ||
| } | ||
| return to; | ||
| }; | ||
| var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); | ||
| // src/webcomponent.ts | ||
| var webcomponent_exports = {}; | ||
| __export(webcomponent_exports, { | ||
| Poline: () => Poline, | ||
| PolinePicker: () => PolinePicker, | ||
| positionFunctions: () => positionFunctions | ||
| }); | ||
| module.exports = __toCommonJS(webcomponent_exports); | ||
| // src/index.ts | ||
| var pointToHSL = (xyz, invertedLightness) => { | ||
| const [x, y, z] = xyz; | ||
| const cx = 0.5; | ||
| const cy = 0.5; | ||
| const radians = Math.atan2(y - cy, x - cx); | ||
| let deg = radians * (180 / Math.PI); | ||
| deg = (360 + deg) % 360; | ||
| const s = z; | ||
| const dist = Math.sqrt(Math.pow(y - cy, 2) + Math.pow(x - cx, 2)); | ||
| const l = dist / cx; | ||
| return [deg, s, invertedLightness ? 1 - l : l]; | ||
| }; | ||
| var hslToPoint = (hsl, invertedLightness) => { | ||
| const [h, s, l] = hsl; | ||
| const cx = 0.5; | ||
| const cy = 0.5; | ||
| const radians = h / (180 / Math.PI); | ||
| const dist = (invertedLightness ? 1 - l : l) * cx; | ||
| const x = cx + dist * Math.cos(radians); | ||
| const y = cy + dist * Math.sin(radians); | ||
| const z = s; | ||
| return [x, y, z]; | ||
| }; | ||
| var randomHSLPair = (startHue = Math.random() * 360, saturations = [Math.random(), Math.random()], lightnesses = [0.75 + Math.random() * 0.2, 0.3 + Math.random() * 0.2]) => [ | ||
| [startHue, saturations[0], lightnesses[0]], | ||
| [(startHue + 60 + Math.random() * 180) % 360, saturations[1], lightnesses[1]] | ||
| ]; | ||
| var vectorOnLine = (t, p1, p2, invert = false, fx = (t2, invert2) => invert2 ? 1 - t2 : t2, fy = (t2, invert2) => invert2 ? 1 - t2 : t2, fz = (t2, invert2) => invert2 ? 1 - t2 : t2) => { | ||
| const tModifiedX = fx(t, invert); | ||
| const tModifiedY = fy(t, invert); | ||
| const tModifiedZ = fz(t, invert); | ||
| const x = (1 - tModifiedX) * p1[0] + tModifiedX * p2[0]; | ||
| const y = (1 - tModifiedY) * p1[1] + tModifiedY * p2[1]; | ||
| const z = (1 - tModifiedZ) * p1[2] + tModifiedZ * p2[2]; | ||
| return [x, y, z]; | ||
| }; | ||
| var vectorsOnLine = (p1, p2, numPoints = 4, invert = false, fx = (t, invert2) => invert2 ? 1 - t : t, fy = (t, invert2) => invert2 ? 1 - t : t, fz = (t, invert2) => invert2 ? 1 - t : t) => { | ||
| const points = []; | ||
| for (let i = 0; i < numPoints; i++) { | ||
| const [x, y, z] = vectorOnLine( | ||
| i / (numPoints - 1), | ||
| p1, | ||
| p2, | ||
| invert, | ||
| fx, | ||
| fy, | ||
| fz | ||
| ); | ||
| points.push([x, y, z]); | ||
| } | ||
| return points; | ||
| }; | ||
| var linearPosition = (t) => { | ||
| return t; | ||
| }; | ||
| var exponentialPosition = (t, reverse = false) => { | ||
| if (reverse) { | ||
| return 1 - __pow(1 - t, 2); | ||
| } | ||
| return __pow(t, 2); | ||
| }; | ||
| var quadraticPosition = (t, reverse = false) => { | ||
| if (reverse) { | ||
| return 1 - __pow(1 - t, 3); | ||
| } | ||
| return __pow(t, 3); | ||
| }; | ||
| var cubicPosition = (t, reverse = false) => { | ||
| if (reverse) { | ||
| return 1 - __pow(1 - t, 4); | ||
| } | ||
| return __pow(t, 4); | ||
| }; | ||
| var quarticPosition = (t, reverse = false) => { | ||
| if (reverse) { | ||
| return 1 - __pow(1 - t, 5); | ||
| } | ||
| return __pow(t, 5); | ||
| }; | ||
| var sinusoidalPosition = (t, reverse = false) => { | ||
| if (reverse) { | ||
| return 1 - Math.sin((1 - t) * Math.PI / 2); | ||
| } | ||
| return Math.sin(t * Math.PI / 2); | ||
| }; | ||
| var asinusoidalPosition = (t, reverse = false) => { | ||
| if (reverse) { | ||
| return 1 - Math.asin(1 - t) / (Math.PI / 2); | ||
| } | ||
| return Math.asin(t) / (Math.PI / 2); | ||
| }; | ||
| var arcPosition = (t, reverse = false) => { | ||
| if (reverse) { | ||
| return 1 - Math.sqrt(1 - __pow(t, 2)); | ||
| } | ||
| return 1 - Math.sqrt(1 - t); | ||
| }; | ||
| var smoothStepPosition = (t) => { | ||
| return __pow(t, 2) * (3 - 2 * t); | ||
| }; | ||
| var positionFunctions = { | ||
| linearPosition, | ||
| exponentialPosition, | ||
| quadraticPosition, | ||
| cubicPosition, | ||
| quarticPosition, | ||
| sinusoidalPosition, | ||
| asinusoidalPosition, | ||
| arcPosition, | ||
| smoothStepPosition | ||
| }; | ||
| var distance = (p1, p2, hueMode = false) => { | ||
| const a1 = p1[0]; | ||
| const a2 = p2[0]; | ||
| let diffA = 0; | ||
| if (hueMode && a1 !== null && a2 !== null) { | ||
| diffA = Math.min(Math.abs(a1 - a2), 360 - Math.abs(a1 - a2)); | ||
| diffA = diffA / 360; | ||
| } else { | ||
| diffA = a1 === null || a2 === null ? 0 : a1 - a2; | ||
| } | ||
| const a = diffA; | ||
| const b = p1[1] === null || p2[1] === null ? 0 : p2[1] - p1[1]; | ||
| const c = p1[2] === null || p2[2] === null ? 0 : p2[2] - p1[2]; | ||
| return Math.sqrt(a * a + b * b + c * c); | ||
| }; | ||
| var ColorPoint = class { | ||
| constructor({ | ||
| xyz, | ||
| color, | ||
| invertedLightness = false | ||
| } = {}) { | ||
| this.x = 0; | ||
| this.y = 0; | ||
| this.z = 0; | ||
| this.color = [0, 0, 0]; | ||
| this._invertedLightness = false; | ||
| this._invertedLightness = invertedLightness; | ||
| this.positionOrColor({ xyz, color, invertedLightness }); | ||
| } | ||
| positionOrColor({ | ||
| xyz, | ||
| color, | ||
| invertedLightness = false | ||
| }) { | ||
| if (xyz && color || !xyz && !color) { | ||
| throw new Error("Point must be initialized with either x,y,z or hsl"); | ||
| } else if (xyz) { | ||
| this.x = xyz[0]; | ||
| this.y = xyz[1]; | ||
| this.z = xyz[2]; | ||
| this.color = pointToHSL([this.x, this.y, this.z], invertedLightness); | ||
| } else if (color) { | ||
| this.color = color; | ||
| [this.x, this.y, this.z] = hslToPoint(color, invertedLightness); | ||
| } | ||
| } | ||
| set position([x, y, z]) { | ||
| this.x = x; | ||
| this.y = y; | ||
| this.z = z; | ||
| this.color = pointToHSL( | ||
| [this.x, this.y, this.z], | ||
| this._invertedLightness | ||
| ); | ||
| } | ||
| get position() { | ||
| return [this.x, this.y, this.z]; | ||
| } | ||
| set hsl([h, s, l]) { | ||
| this.color = [h, s, l]; | ||
| [this.x, this.y, this.z] = hslToPoint( | ||
| this.color, | ||
| this._invertedLightness | ||
| ); | ||
| } | ||
| get hsl() { | ||
| return this.color; | ||
| } | ||
| get hslCSS() { | ||
| const [h, s, l] = this.color; | ||
| return `hsl(${h.toFixed(2)}, ${(s * 100).toFixed(2)}%, ${(l * 100).toFixed( | ||
| 2 | ||
| )}%)`; | ||
| } | ||
| get oklchCSS() { | ||
| const [h, s, l] = this.color; | ||
| return `oklch(${(l * 100).toFixed(2)}% ${(s * 0.4).toFixed(3)} ${h.toFixed( | ||
| 2 | ||
| )})`; | ||
| } | ||
| get lchCSS() { | ||
| const [h, s, l] = this.color; | ||
| return `lch(${(l * 100).toFixed(2)}% ${(s * 150).toFixed(2)} ${h.toFixed( | ||
| 2 | ||
| )})`; | ||
| } | ||
| shiftHue(angle) { | ||
| this.color[0] = (360 + (this.color[0] + angle)) % 360; | ||
| [this.x, this.y, this.z] = hslToPoint( | ||
| this.color, | ||
| this._invertedLightness | ||
| ); | ||
| } | ||
| }; | ||
| var Poline = class { | ||
| constructor({ | ||
| anchorColors = randomHSLPair(), | ||
| numPoints = 4, | ||
| positionFunction = sinusoidalPosition, | ||
| positionFunctionX, | ||
| positionFunctionY, | ||
| positionFunctionZ, | ||
| closedLoop, | ||
| invertedLightness | ||
| } = { | ||
| anchorColors: randomHSLPair(), | ||
| numPoints: 4, | ||
| positionFunction: sinusoidalPosition, | ||
| closedLoop: false | ||
| }) { | ||
| this._needsUpdate = true; | ||
| this._positionFunctionX = sinusoidalPosition; | ||
| this._positionFunctionY = sinusoidalPosition; | ||
| this._positionFunctionZ = sinusoidalPosition; | ||
| this.connectLastAndFirstAnchor = false; | ||
| this._animationFrame = null; | ||
| this._invertedLightness = false; | ||
| if (!anchorColors || anchorColors.length < 2) { | ||
| throw new Error("Must have at least two anchor colors"); | ||
| } | ||
| this._anchorPoints = anchorColors.map( | ||
| (point) => new ColorPoint({ color: point, invertedLightness }) | ||
| ); | ||
| this._numPoints = numPoints + 2; | ||
| this._positionFunctionX = positionFunctionX || positionFunction || sinusoidalPosition; | ||
| this._positionFunctionY = positionFunctionY || positionFunction || sinusoidalPosition; | ||
| this._positionFunctionZ = positionFunctionZ || positionFunction || sinusoidalPosition; | ||
| this.connectLastAndFirstAnchor = closedLoop || false; | ||
| this._invertedLightness = invertedLightness || false; | ||
| this.updateAnchorPairs(); | ||
| } | ||
| get numPoints() { | ||
| return this._numPoints - 2; | ||
| } | ||
| set numPoints(numPoints) { | ||
| if (numPoints < 1) { | ||
| throw new Error("Must have at least one point"); | ||
| } | ||
| this._numPoints = numPoints + 2; | ||
| this.updateAnchorPairs(); | ||
| } | ||
| set positionFunction(positionFunction) { | ||
| if (Array.isArray(positionFunction)) { | ||
| if (positionFunction.length !== 3) { | ||
| throw new Error("Position function array must have 3 elements"); | ||
| } | ||
| if (typeof positionFunction[0] !== "function" || typeof positionFunction[1] !== "function" || typeof positionFunction[2] !== "function") { | ||
| throw new Error("Position function array must have 3 functions"); | ||
| } | ||
| this._positionFunctionX = positionFunction[0]; | ||
| this._positionFunctionY = positionFunction[1]; | ||
| this._positionFunctionZ = positionFunction[2]; | ||
| } else { | ||
| this._positionFunctionX = positionFunction; | ||
| this._positionFunctionY = positionFunction; | ||
| this._positionFunctionZ = positionFunction; | ||
| } | ||
| this.updateAnchorPairs(); | ||
| } | ||
| get positionFunction() { | ||
| if (this._positionFunctionX === this._positionFunctionY && this._positionFunctionX === this._positionFunctionZ) { | ||
| return this._positionFunctionX; | ||
| } | ||
| return [ | ||
| this._positionFunctionX, | ||
| this._positionFunctionY, | ||
| this._positionFunctionZ | ||
| ]; | ||
| } | ||
| set positionFunctionX(positionFunctionX) { | ||
| this._positionFunctionX = positionFunctionX; | ||
| this.updateAnchorPairs(); | ||
| } | ||
| get positionFunctionX() { | ||
| return this._positionFunctionX; | ||
| } | ||
| set positionFunctionY(positionFunctionY) { | ||
| this._positionFunctionY = positionFunctionY; | ||
| this.updateAnchorPairs(); | ||
| } | ||
| get positionFunctionY() { | ||
| return this._positionFunctionY; | ||
| } | ||
| set positionFunctionZ(positionFunctionZ) { | ||
| this._positionFunctionZ = positionFunctionZ; | ||
| this.updateAnchorPairs(); | ||
| } | ||
| get positionFunctionZ() { | ||
| return this._positionFunctionZ; | ||
| } | ||
| get anchorPoints() { | ||
| return this._anchorPoints; | ||
| } | ||
| set anchorPoints(anchorPoints) { | ||
| this._anchorPoints = anchorPoints; | ||
| this.updateAnchorPairs(); | ||
| } | ||
| updateAnchorPairs() { | ||
| this._anchorPairs = []; | ||
| const anchorPointsLength = this.connectLastAndFirstAnchor ? this.anchorPoints.length : this.anchorPoints.length - 1; | ||
| for (let i = 0; i < anchorPointsLength; i++) { | ||
| const pair = [ | ||
| this.anchorPoints[i], | ||
| this.anchorPoints[(i + 1) % this.anchorPoints.length] | ||
| ]; | ||
| this._anchorPairs.push(pair); | ||
| } | ||
| this.points = this._anchorPairs.map((pair, i) => { | ||
| const p1position = pair[0] ? pair[0].position : [0, 0, 0]; | ||
| const p2position = pair[1] ? pair[1].position : [0, 0, 0]; | ||
| return vectorsOnLine( | ||
| p1position, | ||
| p2position, | ||
| this._numPoints, | ||
| i % 2 ? true : false, | ||
| this.positionFunctionX, | ||
| this.positionFunctionY, | ||
| this.positionFunctionZ | ||
| ).map( | ||
| (p) => new ColorPoint({ xyz: p, invertedLightness: this._invertedLightness }) | ||
| ); | ||
| }); | ||
| } | ||
| addAnchorPoint({ | ||
| xyz, | ||
| color, | ||
| insertAtIndex | ||
| }) { | ||
| const newAnchor = new ColorPoint({ | ||
| xyz, | ||
| color, | ||
| invertedLightness: this._invertedLightness | ||
| }); | ||
| if (insertAtIndex !== void 0) { | ||
| this.anchorPoints.splice(insertAtIndex, 0, newAnchor); | ||
| } else { | ||
| this.anchorPoints.push(newAnchor); | ||
| } | ||
| this.updateAnchorPairs(); | ||
| return newAnchor; | ||
| } | ||
| removeAnchorPoint({ | ||
| point, | ||
| index | ||
| }) { | ||
| if (!point && index === 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 apid; | ||
| if (index !== void 0) { | ||
| apid = index; | ||
| } else if (point) { | ||
| apid = this.anchorPoints.indexOf(point); | ||
| } | ||
| if (apid > -1 && apid < this.anchorPoints.length) { | ||
| this.anchorPoints.splice(apid, 1); | ||
| this.updateAnchorPairs(); | ||
| } else { | ||
| throw new Error("Point not found"); | ||
| } | ||
| } | ||
| updateAnchorPoint({ | ||
| point, | ||
| pointIndex, | ||
| xyz, | ||
| color | ||
| }) { | ||
| if (pointIndex !== void 0) { | ||
| point = this.anchorPoints[pointIndex]; | ||
| } | ||
| if (!point) { | ||
| throw new Error("Must provide a point or pointIndex"); | ||
| } | ||
| if (!xyz && !color) { | ||
| throw new Error("Must provide a new xyz position or color"); | ||
| } | ||
| if (xyz) | ||
| point.position = xyz; | ||
| if (color) | ||
| point.hsl = color; | ||
| this.updateAnchorPairs(); | ||
| return point; | ||
| } | ||
| getClosestAnchorPoint({ | ||
| xyz, | ||
| hsl, | ||
| maxDistance = 1 | ||
| }) { | ||
| if (!xyz && !hsl) { | ||
| throw new Error("Must provide a xyz or hsl"); | ||
| } | ||
| let distances; | ||
| if (xyz) { | ||
| distances = this.anchorPoints.map( | ||
| (anchor) => distance(anchor.position, xyz) | ||
| ); | ||
| } else if (hsl) { | ||
| distances = this.anchorPoints.map( | ||
| (anchor) => distance(anchor.hsl, hsl, true) | ||
| ); | ||
| } | ||
| const minDistance = Math.min(...distances); | ||
| if (minDistance > maxDistance) { | ||
| return null; | ||
| } | ||
| const closestAnchorIndex = distances.indexOf(minDistance); | ||
| return this.anchorPoints[closestAnchorIndex] || null; | ||
| } | ||
| set closedLoop(newStatus) { | ||
| this.connectLastAndFirstAnchor = newStatus; | ||
| this.updateAnchorPairs(); | ||
| } | ||
| get closedLoop() { | ||
| return this.connectLastAndFirstAnchor; | ||
| } | ||
| set invertedLightness(newStatus) { | ||
| this._invertedLightness = newStatus; | ||
| this.updateAnchorPairs(); | ||
| } | ||
| get invertedLightness() { | ||
| return this._invertedLightness; | ||
| } | ||
| /** | ||
| * Returns a flattened array of all points across all segments, | ||
| * removing duplicated anchor points at segment boundaries. | ||
| * | ||
| * Since anchor points exist at both the end of one segment and | ||
| * the beginning of the next, this method keeps only one instance of each. | ||
| * The filter logic keeps the first point (index 0) and then filters out | ||
| * points whose indices are multiples of the segment size (_numPoints), | ||
| * which are the anchor points at the start of each segment (except the first). | ||
| * | ||
| * This approach ensures we get all unique points in the correct order | ||
| * while avoiding duplicated anchor points. | ||
| * | ||
| * @returns {ColorPoint[]} A flat array of unique ColorPoint instances | ||
| */ | ||
| get flattenedPoints() { | ||
| return this.points.flat().filter((p, i) => i != 0 ? i % this._numPoints : true); | ||
| } | ||
| get colors() { | ||
| const colors = this.flattenedPoints.map((p) => p.color); | ||
| if (this.connectLastAndFirstAnchor) { | ||
| colors.pop(); | ||
| } | ||
| return colors; | ||
| } | ||
| cssColors(mode = "hsl") { | ||
| const methods = { | ||
| hsl: (p) => p.hslCSS, | ||
| oklch: (p) => p.oklchCSS, | ||
| lch: (p) => p.lchCSS | ||
| }; | ||
| const cssColors = this.flattenedPoints.map(methods[mode]); | ||
| if (this.connectLastAndFirstAnchor) { | ||
| cssColors.pop(); | ||
| } | ||
| return cssColors; | ||
| } | ||
| get colorsCSS() { | ||
| return this.cssColors("hsl"); | ||
| } | ||
| get colorsCSSlch() { | ||
| return this.cssColors("lch"); | ||
| } | ||
| get colorsCSSoklch() { | ||
| return this.cssColors("oklch"); | ||
| } | ||
| shiftHue(hShift = 20) { | ||
| this.anchorPoints.forEach((p) => p.shiftHue(hShift)); | ||
| this.updateAnchorPairs(); | ||
| } | ||
| }; | ||
| var { p5 } = globalThis; | ||
| if (p5) { | ||
| console.info("p5 detected, adding poline to p5 prototype"); | ||
| const poline = new Poline(); | ||
| p5.prototype.poline = poline; | ||
| const polineColors = () => poline.colors.map( | ||
| (c) => `hsl(${Math.round(c[0])},${c[1] * 100}%,${c[2] * 100}%)` | ||
| ); | ||
| p5.prototype.polineColors = polineColors; | ||
| p5.prototype.registerMethod("polineColors", p5.prototype.polineColors); | ||
| globalThis.poline = poline; | ||
| globalThis.polineColors = polineColors; | ||
| } | ||
| // src/webcomponent.ts | ||
| var namespaceURI = "http://www.w3.org/2000/svg"; | ||
| var svgscale = 100; | ||
| var PolinePicker = class extends HTMLElement { | ||
| constructor() { | ||
| super(); | ||
| this.currentPoint = null; | ||
| this.allowAddPoints = false; | ||
| // Store bound event handlers for cleanup | ||
| this.boundPointerDown = this.handlePointerDown.bind(this); | ||
| this.boundPointerMove = this.handlePointerMove.bind(this); | ||
| this.boundPointerUp = this.handlePointerUp.bind(this); | ||
| this.attachShadow({ mode: "open" }); | ||
| this.interactive = this.hasAttribute("interactive"); | ||
| this.allowAddPoints = this.hasAttribute("allow-add-points"); | ||
| } | ||
| connectedCallback() { | ||
| this.render(); | ||
| if (this.interactive) { | ||
| this.addEventListeners(); | ||
| } | ||
| } | ||
| disconnectedCallback() { | ||
| this.removeEventListeners(); | ||
| } | ||
| setPoline(poline) { | ||
| this.poline = poline; | ||
| this.updateSVG(); | ||
| this.updateLightnessBackground(); | ||
| } | ||
| setAllowAddPoints(allow) { | ||
| this.allowAddPoints = allow; | ||
| } | ||
| addPointAtPosition(x, y) { | ||
| if (!this.poline) | ||
| return null; | ||
| const normalizedX = x / this.svg.clientWidth; | ||
| const normalizedY = y / this.svg.clientHeight; | ||
| const newPoint = this.poline.addAnchorPoint({ | ||
| xyz: [normalizedX, normalizedY, normalizedY] | ||
| }); | ||
| this.updateSVG(); | ||
| this.dispatchPolineChange(); | ||
| return newPoint; | ||
| } | ||
| updateLightnessBackground() { | ||
| var _a; | ||
| const picker = (_a = this.shadowRoot) == null ? void 0 : _a.querySelector(".picker"); | ||
| if (picker && this.poline) { | ||
| if (this.poline.invertedLightness) { | ||
| picker.style.setProperty("--maxL", "#000"); | ||
| picker.style.setProperty("--minL", "#fff"); | ||
| } else { | ||
| picker.style.setProperty("--maxL", "#fff"); | ||
| picker.style.setProperty("--minL", "#000"); | ||
| } | ||
| } | ||
| } | ||
| render() { | ||
| if (!this.shadowRoot) { | ||
| return; | ||
| } | ||
| this.shadowRoot.innerHTML = ` | ||
| <style> | ||
| :host { | ||
| display: block; | ||
| width: 100%; | ||
| } | ||
| .picker { | ||
| position: relative; | ||
| width: 100%; | ||
| aspect-ratio: 1; | ||
| --wheelS: var(--poline-picker-wheel-saturation, .4); | ||
| --wheelL: var(--poline-picker-wheel-lightness, .5); | ||
| --minL: #000; | ||
| --maxL: #fff; | ||
| --grad: hsl(0deg calc(var(--wheelS) * 100%) calc(var(--wheelL) * 100%)) 0deg, hsl(60deg calc(var(--wheelS) * 100%) calc(var(--wheelL) * 100%)) 60deg, hsl(120deg calc(var(--wheelS) * 100%) calc(var(--wheelL) * 100%)) 120deg, hsl(180deg calc(var(--wheelS) * 100%) calc(var(--wheelL) * 100%)) 180deg, hsl(240deg calc(var(--wheelS) * 100%) calc(var(--wheelL) * 100%)) 240deg, hsl(300deg calc(var(--wheelS) * 100%) calc(var(--wheelL) * 100%)) 300deg, hsl(360deg calc(var(--wheelS) * 100%) calc(var(--wheelL) * 100%)) 360deg; | ||
| } | ||
| .picker::before { | ||
| content: ''; | ||
| position: absolute; | ||
| inset: 0; | ||
| border-radius: 50%; | ||
| background: radial-gradient(closest-side, var(--minL), rgba(255, 255, 255, 0), var(--maxL)), | ||
| conic-gradient(from 90deg, var(--grad)); | ||
| z-index: 1; | ||
| } | ||
| svg { | ||
| position: relative; | ||
| z-index: 2; | ||
| overflow: visible; | ||
| width: 100%; | ||
| } | ||
| .wheel__line { | ||
| stroke: var(--poline-picker-line-color, #000); | ||
| stroke-width: calc(0.75 * var(--poline-picker-line-width, 0.2)); | ||
| fill: none; | ||
| } | ||
| .wheel__anchor { | ||
| cursor: grab; | ||
| stroke: var(--poline-picker-line-color, #000); | ||
| stroke-width: var(--poline-picker-line-width, 0.2); | ||
| fill: var(--poline-picker-bg-color, #fff); | ||
| } | ||
| .wheel__anchor:hover { | ||
| cursor: grabbing; | ||
| } | ||
| .wheel__point { | ||
| stroke: var(--poline-picker-line-color, #000); | ||
| stroke-width: calc(0.75 * var(--poline-picker-line-width, 0.2)); | ||
| pointer-events: none; | ||
| } | ||
| </style> | ||
| `; | ||
| this.svg = this.createSVG(); | ||
| const pickerDiv = document.createElement("div"); | ||
| pickerDiv.className = "picker"; | ||
| pickerDiv.appendChild(this.svg); | ||
| this.shadowRoot.appendChild(pickerDiv); | ||
| this.wheel = this.svg.querySelector(".wheel"); | ||
| this.line = this.svg.querySelector(".wheel__line"); | ||
| this.anchors = this.svg.querySelector(".wheel__anchors"); | ||
| this.points = this.svg.querySelector(".wheel__points"); | ||
| if (this.poline) { | ||
| this.updateSVG(); | ||
| } | ||
| } | ||
| createSVG() { | ||
| const svg = document.createElementNS(namespaceURI, "svg"); | ||
| svg.setAttribute("viewBox", `0 0 ${svgscale} ${svgscale}`); | ||
| svg.innerHTML = ` | ||
| <defs> | ||
| <filter id="goo"> | ||
| <feGaussianBlur in="SourceGraphic" stdDeviation="1" result="blur" /> | ||
| <feColorMatrix in="blur" mode="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 18 -7" result="goo" /> | ||
| <feBlend in="SourceGraphic" in2="goo" /> | ||
| </filter> | ||
| </defs> | ||
| <g class="wheel" filter="url(#goo)"> | ||
| <polyline class="wheel__line" points="" /> | ||
| <g class="wheel__anchors"></g> | ||
| <g class="wheel__points"></g> | ||
| </g> | ||
| `; | ||
| return svg; | ||
| } | ||
| updateSVG() { | ||
| if (!this.poline || !this.svg) { | ||
| return; | ||
| } | ||
| const pathPoints = this.poline.flattenedPoints.map((p) => { | ||
| const cartesian = this.pointToCartesian(p); | ||
| if (!cartesian) | ||
| return ""; | ||
| const [x, y] = cartesian; | ||
| return `${x},${y}`; | ||
| }).filter((point) => point !== "").join(" "); | ||
| this.line.setAttribute("points", pathPoints); | ||
| this.anchors.innerHTML = ""; | ||
| this.points.innerHTML = ""; | ||
| this.poline.anchorPoints.forEach((point) => { | ||
| const anchor = this.createCircleElement(point, "wheel__anchor", "2"); | ||
| if (anchor) { | ||
| this.anchors.appendChild(anchor); | ||
| } | ||
| }); | ||
| this.poline.flattenedPoints.forEach((point) => { | ||
| const radius = 0.5 + point.color[1]; | ||
| const circle = this.createCircleElement(point, "wheel__point", radius); | ||
| if (circle) { | ||
| this.points.appendChild(circle); | ||
| } | ||
| }); | ||
| } | ||
| pointToCartesian(point) { | ||
| const half = svgscale / 2; | ||
| const x = half + (point.x - 0.5) * svgscale; | ||
| const y = half + (point.y - 0.5) * svgscale; | ||
| return [x, y]; | ||
| } | ||
| addEventListeners() { | ||
| if (!this.svg) | ||
| return; | ||
| this.svg.addEventListener("pointerdown", this.boundPointerDown); | ||
| this.svg.addEventListener("pointermove", this.boundPointerMove); | ||
| this.svg.addEventListener("pointerup", this.boundPointerUp); | ||
| } | ||
| removeEventListeners() { | ||
| if (!this.svg) | ||
| return; | ||
| this.svg.removeEventListener("pointerdown", this.boundPointerDown); | ||
| this.svg.removeEventListener("pointermove", this.boundPointerMove); | ||
| this.svg.removeEventListener("pointerup", this.boundPointerUp); | ||
| } | ||
| handlePointerDown(e) { | ||
| e.stopPropagation(); | ||
| const { normalizedX, normalizedY } = this.pointerToNormalizedCoordinates(e); | ||
| const closestAnchor = this.poline.getClosestAnchorPoint({ | ||
| xyz: [normalizedX, normalizedY, null], | ||
| maxDistance: 0.1 | ||
| }); | ||
| if (closestAnchor) { | ||
| this.currentPoint = closestAnchor; | ||
| } else if (this.allowAddPoints) { | ||
| this.currentPoint = this.poline.addAnchorPoint({ | ||
| xyz: [normalizedX, normalizedY, normalizedY] | ||
| }); | ||
| this.updateSVG(); | ||
| this.dispatchPolineChange(); | ||
| } | ||
| } | ||
| handlePointerMove(e) { | ||
| if (this.currentPoint) { | ||
| const { normalizedX, normalizedY } = this.pointerToNormalizedCoordinates(e); | ||
| this.poline.updateAnchorPoint({ | ||
| point: this.currentPoint, | ||
| xyz: [normalizedX, normalizedY, this.currentPoint.z] | ||
| }); | ||
| this.updateSVG(); | ||
| this.dispatchPolineChange(); | ||
| } | ||
| } | ||
| handlePointerUp() { | ||
| this.currentPoint = null; | ||
| } | ||
| getPointerPosition(e) { | ||
| const rect = this.svg.getBoundingClientRect(); | ||
| return { | ||
| x: e.clientX - rect.left, | ||
| y: e.clientY - rect.top | ||
| }; | ||
| } | ||
| pointerToNormalizedCoordinates(e) { | ||
| const svgRect = this.svg.getBoundingClientRect(); | ||
| const svgX = (e.clientX - svgRect.left) / svgRect.width * svgscale; | ||
| const svgY = (e.clientY - svgRect.top) / svgRect.height * svgscale; | ||
| return { | ||
| normalizedX: svgX / svgscale, | ||
| normalizedY: svgY / svgscale | ||
| }; | ||
| } | ||
| createCircleElement(point, className, radius) { | ||
| const cartesian = this.pointToCartesian(point); | ||
| if (!cartesian) | ||
| return null; | ||
| const [x = 0, y = 0] = cartesian; | ||
| const circle = document.createElementNS(namespaceURI, "circle"); | ||
| circle.setAttribute("class", className); | ||
| circle.setAttribute("cx", x.toString()); | ||
| circle.setAttribute("cy", y.toString()); | ||
| circle.setAttribute("r", radius.toString()); | ||
| circle.setAttribute("fill", point.hslCSS); | ||
| return circle; | ||
| } | ||
| dispatchPolineChange() { | ||
| this.dispatchEvent( | ||
| new CustomEvent("poline-change", { | ||
| detail: { poline: this.poline } | ||
| }) | ||
| ); | ||
| } | ||
| }; | ||
| customElements.define("poline-picker", PolinePicker); |
+165
| <!DOCTYPE html> | ||
| <html lang="en"> | ||
| <head> | ||
| <meta charset="UTF-8" /> | ||
| <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||
| <title>Poline Picker Example</title> | ||
| <link rel="preconnect" href="https://fonts.googleapis.com"> | ||
| <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin=""> | ||
| <link href="https://fonts.googleapis.com/css2?family=Aboreto&family=Work+Sans:wght@300;400&display=swap" | ||
| rel="stylesheet"> | ||
| <style> | ||
| body { | ||
| font-family: 'Work Sans', sans-serif; | ||
| margin: 0; | ||
| padding: 2rem; | ||
| background: #f0f0f0; | ||
| } | ||
| .container { | ||
| display: flex; | ||
| flex-direction: column; | ||
| align-items: center; | ||
| gap: 20px; | ||
| } | ||
| .controls label { | ||
| display: block; | ||
| margin-top: 0.5rem; | ||
| } | ||
| poline-picker { | ||
| width: 300px; | ||
| height: 300px; | ||
| --poline-picker-line-color: #333; | ||
| --poline-picker-bg-color: #fff; | ||
| } | ||
| #colors { | ||
| display: flex; | ||
| margin-top: 20px; | ||
| width: 300px; | ||
| height: 10px; | ||
| gap: 0.1em; | ||
| } | ||
| .color-swatch { | ||
| flex: 1 0 auto; | ||
| } | ||
| h1 { | ||
| color: #333; | ||
| font-family: 'Aboreto', cursive; | ||
| font-size: 2.5rem; | ||
| margin: 0; | ||
| padding: 0; | ||
| font-weight: normal; | ||
| letter-spacing: -0.05em; | ||
| margin-left: -0.06em; | ||
| } | ||
| .instructions { | ||
| color: #666; | ||
| max-width: 400px; | ||
| line-height: 1.5; | ||
| } | ||
| </style> | ||
| </head> | ||
| <body> | ||
| <div class="container"> | ||
| <h1>Poline Picker</h1> | ||
| <p class="instructions"> | ||
| Drag existing anchor points to adjust colors. Press 'P' to add a new point at cursor position, | ||
| or use the "Add Points" checkbox to enable click-to-add mode. | ||
| </p> | ||
| <div class="controls"> | ||
| <label> | ||
| <input type="checkbox" id="allowAddPoints"> Allow Add Points (Click) | ||
| </label> | ||
| <label> | ||
| <input type="checkbox" id="invertedLightness"> Inverted Lightness | ||
| </label> | ||
| <label> | ||
| <input type="checkbox" id="closedLoop"> Closed Loop | ||
| </label> | ||
| </div> | ||
| <poline-picker interactive></poline-picker> | ||
| <div id="colors"></div> | ||
| </div> | ||
| <script type="module"> | ||
| import { Poline, positionFunctions } from "./picker.mjs"; | ||
| const picker = document.querySelector("poline-picker"); | ||
| const colorsContainer = document.getElementById("colors"); | ||
| const allowAddPointsCheckbox = document.getElementById("allowAddPoints"); | ||
| const invertedLightnessCheckbox = document.getElementById("invertedLightness"); | ||
| const closedLoopCheckbox = document.getElementById("closedLoop"); | ||
| const poline = new Poline({ | ||
| anchorColors: [ | ||
| [0, 1, 0.5], | ||
| [180, 1, 0.5], | ||
| ], | ||
| numPoints: 5, | ||
| positionFunctionX: positionFunctions.smoothStepPosition, | ||
| positionFunctionY: positionFunctions.smoothStepPosition, | ||
| positionFunctionZ: positionFunctions.linearPosition, | ||
| closedLoop: false, | ||
| invertedLightness: false, | ||
| }); | ||
| picker.setPoline(poline); | ||
| let lastMousePosition = { x: 0, y: 0 }; | ||
| function updateColors() { | ||
| const colors = poline.colorsCSS; | ||
| colorsContainer.innerHTML = ""; | ||
| for (const color of colors) { | ||
| const swatch = document.createElement("div"); | ||
| swatch.className = "color-swatch"; | ||
| swatch.style.backgroundColor = color; | ||
| swatch.title = color; | ||
| colorsContainer.appendChild(swatch); | ||
| } | ||
| } | ||
| // Track mouse position for 'C' key functionality | ||
| picker.addEventListener("pointermove", (e) => { | ||
| const rect = picker.getBoundingClientRect(); | ||
| lastMousePosition = { | ||
| x: e.clientX - rect.left, | ||
| y: e.clientY - rect.top, | ||
| }; | ||
| }); | ||
| // Listen for the custom event from the picker | ||
| picker.addEventListener("poline-change", updateColors); | ||
| // Handle allow add points toggle | ||
| allowAddPointsCheckbox.addEventListener("change", (e) => { | ||
| picker.setAllowAddPoints(e.target.checked); | ||
| }); | ||
| // Handle inverted lightness toggle | ||
| invertedLightnessCheckbox.addEventListener("change", (e) => { | ||
| poline.invertedLightness = e.target.checked; | ||
| picker.setPoline(poline); // This will update the background | ||
| updateColors(); | ||
| }); | ||
| // Handle closed loop toggle | ||
| closedLoopCheckbox.addEventListener("change", (e) => { | ||
| poline.closedLoop = e.target.checked; | ||
| picker.setPoline(poline); | ||
| updateColors(); | ||
| }); | ||
| // Handle 'P' key to add points like in the demo | ||
| document.addEventListener("keydown", (e) => { | ||
| if (e.key === "p" || e.key === "P") { | ||
| picker.addPointAtPosition(lastMousePosition.x, lastMousePosition.y); | ||
| updateColors(); | ||
| } | ||
| }); | ||
| // Initial color update | ||
| updateColors(); | ||
| </script> | ||
| </body> | ||
| </html> |
+795
| "use strict"; | ||
| var polinePicker = (() => { | ||
| var __defProp = Object.defineProperty; | ||
| var __getOwnPropDesc = Object.getOwnPropertyDescriptor; | ||
| var __getOwnPropNames = Object.getOwnPropertyNames; | ||
| var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
| var __export = (target, all) => { | ||
| for (var name in all) | ||
| __defProp(target, name, { get: all[name], enumerable: true }); | ||
| }; | ||
| var __copyProps = (to, from, except, desc) => { | ||
| if (from && typeof from === "object" || typeof from === "function") { | ||
| for (let key of __getOwnPropNames(from)) | ||
| if (!__hasOwnProp.call(to, key) && key !== except) | ||
| __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); | ||
| } | ||
| return to; | ||
| }; | ||
| var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); | ||
| // src/webcomponent.ts | ||
| var webcomponent_exports = {}; | ||
| __export(webcomponent_exports, { | ||
| Poline: () => Poline, | ||
| PolinePicker: () => PolinePicker, | ||
| positionFunctions: () => positionFunctions | ||
| }); | ||
| // src/index.ts | ||
| var pointToHSL = (xyz, invertedLightness) => { | ||
| const [x, y, z] = xyz; | ||
| const cx = 0.5; | ||
| const cy = 0.5; | ||
| const radians = Math.atan2(y - cy, x - cx); | ||
| let deg = radians * (180 / Math.PI); | ||
| deg = (360 + deg) % 360; | ||
| const s = z; | ||
| const dist = Math.sqrt(Math.pow(y - cy, 2) + Math.pow(x - cx, 2)); | ||
| const l = dist / cx; | ||
| return [deg, s, invertedLightness ? 1 - l : l]; | ||
| }; | ||
| var hslToPoint = (hsl, invertedLightness) => { | ||
| const [h, s, l] = hsl; | ||
| const cx = 0.5; | ||
| const cy = 0.5; | ||
| const radians = h / (180 / Math.PI); | ||
| const dist = (invertedLightness ? 1 - l : l) * cx; | ||
| const x = cx + dist * Math.cos(radians); | ||
| const y = cy + dist * Math.sin(radians); | ||
| const z = s; | ||
| return [x, y, z]; | ||
| }; | ||
| var randomHSLPair = (startHue = Math.random() * 360, saturations = [Math.random(), Math.random()], lightnesses = [0.75 + Math.random() * 0.2, 0.3 + Math.random() * 0.2]) => [ | ||
| [startHue, saturations[0], lightnesses[0]], | ||
| [(startHue + 60 + Math.random() * 180) % 360, saturations[1], lightnesses[1]] | ||
| ]; | ||
| var vectorOnLine = (t, p1, p2, invert = false, fx = (t2, invert2) => invert2 ? 1 - t2 : t2, fy = (t2, invert2) => invert2 ? 1 - t2 : t2, fz = (t2, invert2) => invert2 ? 1 - t2 : t2) => { | ||
| const tModifiedX = fx(t, invert); | ||
| const tModifiedY = fy(t, invert); | ||
| const tModifiedZ = fz(t, invert); | ||
| const x = (1 - tModifiedX) * p1[0] + tModifiedX * p2[0]; | ||
| const y = (1 - tModifiedY) * p1[1] + tModifiedY * p2[1]; | ||
| const z = (1 - tModifiedZ) * p1[2] + tModifiedZ * p2[2]; | ||
| return [x, y, z]; | ||
| }; | ||
| var vectorsOnLine = (p1, p2, numPoints = 4, invert = false, fx = (t, invert2) => invert2 ? 1 - t : t, fy = (t, invert2) => invert2 ? 1 - t : t, fz = (t, invert2) => invert2 ? 1 - t : t) => { | ||
| const points = []; | ||
| for (let i = 0; i < numPoints; i++) { | ||
| const [x, y, z] = vectorOnLine( | ||
| i / (numPoints - 1), | ||
| p1, | ||
| p2, | ||
| invert, | ||
| fx, | ||
| fy, | ||
| fz | ||
| ); | ||
| points.push([x, y, z]); | ||
| } | ||
| return points; | ||
| }; | ||
| var linearPosition = (t) => { | ||
| return t; | ||
| }; | ||
| var exponentialPosition = (t, reverse = false) => { | ||
| if (reverse) { | ||
| return 1 - (1 - t) ** 2; | ||
| } | ||
| return t ** 2; | ||
| }; | ||
| var quadraticPosition = (t, reverse = false) => { | ||
| if (reverse) { | ||
| return 1 - (1 - t) ** 3; | ||
| } | ||
| return t ** 3; | ||
| }; | ||
| var cubicPosition = (t, reverse = false) => { | ||
| if (reverse) { | ||
| return 1 - (1 - t) ** 4; | ||
| } | ||
| return t ** 4; | ||
| }; | ||
| var quarticPosition = (t, reverse = false) => { | ||
| if (reverse) { | ||
| return 1 - (1 - t) ** 5; | ||
| } | ||
| return t ** 5; | ||
| }; | ||
| var sinusoidalPosition = (t, reverse = false) => { | ||
| if (reverse) { | ||
| return 1 - Math.sin((1 - t) * Math.PI / 2); | ||
| } | ||
| return Math.sin(t * Math.PI / 2); | ||
| }; | ||
| var asinusoidalPosition = (t, reverse = false) => { | ||
| if (reverse) { | ||
| return 1 - Math.asin(1 - t) / (Math.PI / 2); | ||
| } | ||
| return Math.asin(t) / (Math.PI / 2); | ||
| }; | ||
| var arcPosition = (t, reverse = false) => { | ||
| if (reverse) { | ||
| return 1 - Math.sqrt(1 - t ** 2); | ||
| } | ||
| return 1 - Math.sqrt(1 - t); | ||
| }; | ||
| var smoothStepPosition = (t) => { | ||
| return t ** 2 * (3 - 2 * t); | ||
| }; | ||
| var positionFunctions = { | ||
| linearPosition, | ||
| exponentialPosition, | ||
| quadraticPosition, | ||
| cubicPosition, | ||
| quarticPosition, | ||
| sinusoidalPosition, | ||
| asinusoidalPosition, | ||
| arcPosition, | ||
| smoothStepPosition | ||
| }; | ||
| var distance = (p1, p2, hueMode = false) => { | ||
| const a1 = p1[0]; | ||
| const a2 = p2[0]; | ||
| let diffA = 0; | ||
| if (hueMode && a1 !== null && a2 !== null) { | ||
| diffA = Math.min(Math.abs(a1 - a2), 360 - Math.abs(a1 - a2)); | ||
| diffA = diffA / 360; | ||
| } else { | ||
| diffA = a1 === null || a2 === null ? 0 : a1 - a2; | ||
| } | ||
| const a = diffA; | ||
| const b = p1[1] === null || p2[1] === null ? 0 : p2[1] - p1[1]; | ||
| const c = p1[2] === null || p2[2] === null ? 0 : p2[2] - p1[2]; | ||
| return Math.sqrt(a * a + b * b + c * c); | ||
| }; | ||
| var ColorPoint = class { | ||
| constructor({ | ||
| xyz, | ||
| color, | ||
| invertedLightness = false | ||
| } = {}) { | ||
| this.x = 0; | ||
| this.y = 0; | ||
| this.z = 0; | ||
| this.color = [0, 0, 0]; | ||
| this._invertedLightness = false; | ||
| this._invertedLightness = invertedLightness; | ||
| this.positionOrColor({ xyz, color, invertedLightness }); | ||
| } | ||
| positionOrColor({ | ||
| xyz, | ||
| color, | ||
| invertedLightness = false | ||
| }) { | ||
| if (xyz && color || !xyz && !color) { | ||
| throw new Error("Point must be initialized with either x,y,z or hsl"); | ||
| } else if (xyz) { | ||
| this.x = xyz[0]; | ||
| this.y = xyz[1]; | ||
| this.z = xyz[2]; | ||
| this.color = pointToHSL([this.x, this.y, this.z], invertedLightness); | ||
| } else if (color) { | ||
| this.color = color; | ||
| [this.x, this.y, this.z] = hslToPoint(color, invertedLightness); | ||
| } | ||
| } | ||
| set position([x, y, z]) { | ||
| this.x = x; | ||
| this.y = y; | ||
| this.z = z; | ||
| this.color = pointToHSL( | ||
| [this.x, this.y, this.z], | ||
| this._invertedLightness | ||
| ); | ||
| } | ||
| get position() { | ||
| return [this.x, this.y, this.z]; | ||
| } | ||
| set hsl([h, s, l]) { | ||
| this.color = [h, s, l]; | ||
| [this.x, this.y, this.z] = hslToPoint( | ||
| this.color, | ||
| this._invertedLightness | ||
| ); | ||
| } | ||
| get hsl() { | ||
| return this.color; | ||
| } | ||
| get hslCSS() { | ||
| const [h, s, l] = this.color; | ||
| return `hsl(${h.toFixed(2)}, ${(s * 100).toFixed(2)}%, ${(l * 100).toFixed( | ||
| 2 | ||
| )}%)`; | ||
| } | ||
| get oklchCSS() { | ||
| const [h, s, l] = this.color; | ||
| return `oklch(${(l * 100).toFixed(2)}% ${(s * 0.4).toFixed(3)} ${h.toFixed( | ||
| 2 | ||
| )})`; | ||
| } | ||
| get lchCSS() { | ||
| const [h, s, l] = this.color; | ||
| return `lch(${(l * 100).toFixed(2)}% ${(s * 150).toFixed(2)} ${h.toFixed( | ||
| 2 | ||
| )})`; | ||
| } | ||
| shiftHue(angle) { | ||
| this.color[0] = (360 + (this.color[0] + angle)) % 360; | ||
| [this.x, this.y, this.z] = hslToPoint( | ||
| this.color, | ||
| this._invertedLightness | ||
| ); | ||
| } | ||
| }; | ||
| var Poline = class { | ||
| constructor({ | ||
| anchorColors = randomHSLPair(), | ||
| numPoints = 4, | ||
| positionFunction = sinusoidalPosition, | ||
| positionFunctionX, | ||
| positionFunctionY, | ||
| positionFunctionZ, | ||
| closedLoop, | ||
| invertedLightness | ||
| } = { | ||
| anchorColors: randomHSLPair(), | ||
| numPoints: 4, | ||
| positionFunction: sinusoidalPosition, | ||
| closedLoop: false | ||
| }) { | ||
| this._needsUpdate = true; | ||
| this._positionFunctionX = sinusoidalPosition; | ||
| this._positionFunctionY = sinusoidalPosition; | ||
| this._positionFunctionZ = sinusoidalPosition; | ||
| this.connectLastAndFirstAnchor = false; | ||
| this._animationFrame = null; | ||
| this._invertedLightness = false; | ||
| if (!anchorColors || anchorColors.length < 2) { | ||
| throw new Error("Must have at least two anchor colors"); | ||
| } | ||
| this._anchorPoints = anchorColors.map( | ||
| (point) => new ColorPoint({ color: point, invertedLightness }) | ||
| ); | ||
| this._numPoints = numPoints + 2; | ||
| this._positionFunctionX = positionFunctionX || positionFunction || sinusoidalPosition; | ||
| this._positionFunctionY = positionFunctionY || positionFunction || sinusoidalPosition; | ||
| this._positionFunctionZ = positionFunctionZ || positionFunction || sinusoidalPosition; | ||
| this.connectLastAndFirstAnchor = closedLoop || false; | ||
| this._invertedLightness = invertedLightness || false; | ||
| this.updateAnchorPairs(); | ||
| } | ||
| get numPoints() { | ||
| return this._numPoints - 2; | ||
| } | ||
| set numPoints(numPoints) { | ||
| if (numPoints < 1) { | ||
| throw new Error("Must have at least one point"); | ||
| } | ||
| this._numPoints = numPoints + 2; | ||
| this.updateAnchorPairs(); | ||
| } | ||
| set positionFunction(positionFunction) { | ||
| if (Array.isArray(positionFunction)) { | ||
| if (positionFunction.length !== 3) { | ||
| throw new Error("Position function array must have 3 elements"); | ||
| } | ||
| if (typeof positionFunction[0] !== "function" || typeof positionFunction[1] !== "function" || typeof positionFunction[2] !== "function") { | ||
| throw new Error("Position function array must have 3 functions"); | ||
| } | ||
| this._positionFunctionX = positionFunction[0]; | ||
| this._positionFunctionY = positionFunction[1]; | ||
| this._positionFunctionZ = positionFunction[2]; | ||
| } else { | ||
| this._positionFunctionX = positionFunction; | ||
| this._positionFunctionY = positionFunction; | ||
| this._positionFunctionZ = positionFunction; | ||
| } | ||
| this.updateAnchorPairs(); | ||
| } | ||
| get positionFunction() { | ||
| if (this._positionFunctionX === this._positionFunctionY && this._positionFunctionX === this._positionFunctionZ) { | ||
| return this._positionFunctionX; | ||
| } | ||
| return [ | ||
| this._positionFunctionX, | ||
| this._positionFunctionY, | ||
| this._positionFunctionZ | ||
| ]; | ||
| } | ||
| set positionFunctionX(positionFunctionX) { | ||
| this._positionFunctionX = positionFunctionX; | ||
| this.updateAnchorPairs(); | ||
| } | ||
| get positionFunctionX() { | ||
| return this._positionFunctionX; | ||
| } | ||
| set positionFunctionY(positionFunctionY) { | ||
| this._positionFunctionY = positionFunctionY; | ||
| this.updateAnchorPairs(); | ||
| } | ||
| get positionFunctionY() { | ||
| return this._positionFunctionY; | ||
| } | ||
| set positionFunctionZ(positionFunctionZ) { | ||
| this._positionFunctionZ = positionFunctionZ; | ||
| this.updateAnchorPairs(); | ||
| } | ||
| get positionFunctionZ() { | ||
| return this._positionFunctionZ; | ||
| } | ||
| get anchorPoints() { | ||
| return this._anchorPoints; | ||
| } | ||
| set anchorPoints(anchorPoints) { | ||
| this._anchorPoints = anchorPoints; | ||
| this.updateAnchorPairs(); | ||
| } | ||
| updateAnchorPairs() { | ||
| this._anchorPairs = []; | ||
| const anchorPointsLength = this.connectLastAndFirstAnchor ? this.anchorPoints.length : this.anchorPoints.length - 1; | ||
| for (let i = 0; i < anchorPointsLength; i++) { | ||
| const pair = [ | ||
| this.anchorPoints[i], | ||
| this.anchorPoints[(i + 1) % this.anchorPoints.length] | ||
| ]; | ||
| this._anchorPairs.push(pair); | ||
| } | ||
| this.points = this._anchorPairs.map((pair, i) => { | ||
| const p1position = pair[0] ? pair[0].position : [0, 0, 0]; | ||
| const p2position = pair[1] ? pair[1].position : [0, 0, 0]; | ||
| return vectorsOnLine( | ||
| p1position, | ||
| p2position, | ||
| this._numPoints, | ||
| i % 2 ? true : false, | ||
| this.positionFunctionX, | ||
| this.positionFunctionY, | ||
| this.positionFunctionZ | ||
| ).map( | ||
| (p) => new ColorPoint({ xyz: p, invertedLightness: this._invertedLightness }) | ||
| ); | ||
| }); | ||
| } | ||
| addAnchorPoint({ | ||
| xyz, | ||
| color, | ||
| insertAtIndex | ||
| }) { | ||
| const newAnchor = new ColorPoint({ | ||
| xyz, | ||
| color, | ||
| invertedLightness: this._invertedLightness | ||
| }); | ||
| if (insertAtIndex !== void 0) { | ||
| this.anchorPoints.splice(insertAtIndex, 0, newAnchor); | ||
| } else { | ||
| this.anchorPoints.push(newAnchor); | ||
| } | ||
| this.updateAnchorPairs(); | ||
| return newAnchor; | ||
| } | ||
| removeAnchorPoint({ | ||
| point, | ||
| index | ||
| }) { | ||
| if (!point && index === 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 apid; | ||
| if (index !== void 0) { | ||
| apid = index; | ||
| } else if (point) { | ||
| apid = this.anchorPoints.indexOf(point); | ||
| } | ||
| if (apid > -1 && apid < this.anchorPoints.length) { | ||
| this.anchorPoints.splice(apid, 1); | ||
| this.updateAnchorPairs(); | ||
| } else { | ||
| throw new Error("Point not found"); | ||
| } | ||
| } | ||
| updateAnchorPoint({ | ||
| point, | ||
| pointIndex, | ||
| xyz, | ||
| color | ||
| }) { | ||
| if (pointIndex !== void 0) { | ||
| point = this.anchorPoints[pointIndex]; | ||
| } | ||
| if (!point) { | ||
| throw new Error("Must provide a point or pointIndex"); | ||
| } | ||
| if (!xyz && !color) { | ||
| throw new Error("Must provide a new xyz position or color"); | ||
| } | ||
| if (xyz) | ||
| point.position = xyz; | ||
| if (color) | ||
| point.hsl = color; | ||
| this.updateAnchorPairs(); | ||
| return point; | ||
| } | ||
| getClosestAnchorPoint({ | ||
| xyz, | ||
| hsl, | ||
| maxDistance = 1 | ||
| }) { | ||
| if (!xyz && !hsl) { | ||
| throw new Error("Must provide a xyz or hsl"); | ||
| } | ||
| let distances; | ||
| if (xyz) { | ||
| distances = this.anchorPoints.map( | ||
| (anchor) => distance(anchor.position, xyz) | ||
| ); | ||
| } else if (hsl) { | ||
| distances = this.anchorPoints.map( | ||
| (anchor) => distance(anchor.hsl, hsl, true) | ||
| ); | ||
| } | ||
| const minDistance = Math.min(...distances); | ||
| if (minDistance > maxDistance) { | ||
| return null; | ||
| } | ||
| const closestAnchorIndex = distances.indexOf(minDistance); | ||
| return this.anchorPoints[closestAnchorIndex] || null; | ||
| } | ||
| set closedLoop(newStatus) { | ||
| this.connectLastAndFirstAnchor = newStatus; | ||
| this.updateAnchorPairs(); | ||
| } | ||
| get closedLoop() { | ||
| return this.connectLastAndFirstAnchor; | ||
| } | ||
| set invertedLightness(newStatus) { | ||
| this._invertedLightness = newStatus; | ||
| this.updateAnchorPairs(); | ||
| } | ||
| get invertedLightness() { | ||
| return this._invertedLightness; | ||
| } | ||
| /** | ||
| * Returns a flattened array of all points across all segments, | ||
| * removing duplicated anchor points at segment boundaries. | ||
| * | ||
| * Since anchor points exist at both the end of one segment and | ||
| * the beginning of the next, this method keeps only one instance of each. | ||
| * The filter logic keeps the first point (index 0) and then filters out | ||
| * points whose indices are multiples of the segment size (_numPoints), | ||
| * which are the anchor points at the start of each segment (except the first). | ||
| * | ||
| * This approach ensures we get all unique points in the correct order | ||
| * while avoiding duplicated anchor points. | ||
| * | ||
| * @returns {ColorPoint[]} A flat array of unique ColorPoint instances | ||
| */ | ||
| get flattenedPoints() { | ||
| return this.points.flat().filter((p, i) => i != 0 ? i % this._numPoints : true); | ||
| } | ||
| get colors() { | ||
| const colors = this.flattenedPoints.map((p) => p.color); | ||
| if (this.connectLastAndFirstAnchor) { | ||
| colors.pop(); | ||
| } | ||
| return colors; | ||
| } | ||
| cssColors(mode = "hsl") { | ||
| const methods = { | ||
| hsl: (p) => p.hslCSS, | ||
| oklch: (p) => p.oklchCSS, | ||
| lch: (p) => p.lchCSS | ||
| }; | ||
| const cssColors = this.flattenedPoints.map(methods[mode]); | ||
| if (this.connectLastAndFirstAnchor) { | ||
| cssColors.pop(); | ||
| } | ||
| return cssColors; | ||
| } | ||
| get colorsCSS() { | ||
| return this.cssColors("hsl"); | ||
| } | ||
| get colorsCSSlch() { | ||
| return this.cssColors("lch"); | ||
| } | ||
| get colorsCSSoklch() { | ||
| return this.cssColors("oklch"); | ||
| } | ||
| shiftHue(hShift = 20) { | ||
| this.anchorPoints.forEach((p) => p.shiftHue(hShift)); | ||
| this.updateAnchorPairs(); | ||
| } | ||
| }; | ||
| var { p5 } = globalThis; | ||
| if (p5) { | ||
| console.info("p5 detected, adding poline to p5 prototype"); | ||
| const poline = new Poline(); | ||
| p5.prototype.poline = poline; | ||
| const polineColors = () => poline.colors.map( | ||
| (c) => `hsl(${Math.round(c[0])},${c[1] * 100}%,${c[2] * 100}%)` | ||
| ); | ||
| p5.prototype.polineColors = polineColors; | ||
| p5.prototype.registerMethod("polineColors", p5.prototype.polineColors); | ||
| globalThis.poline = poline; | ||
| globalThis.polineColors = polineColors; | ||
| } | ||
| // src/webcomponent.ts | ||
| var namespaceURI = "http://www.w3.org/2000/svg"; | ||
| var svgscale = 100; | ||
| var PolinePicker = class extends HTMLElement { | ||
| constructor() { | ||
| super(); | ||
| this.currentPoint = null; | ||
| this.allowAddPoints = false; | ||
| // Store bound event handlers for cleanup | ||
| this.boundPointerDown = this.handlePointerDown.bind(this); | ||
| this.boundPointerMove = this.handlePointerMove.bind(this); | ||
| this.boundPointerUp = this.handlePointerUp.bind(this); | ||
| this.attachShadow({ mode: "open" }); | ||
| this.interactive = this.hasAttribute("interactive"); | ||
| this.allowAddPoints = this.hasAttribute("allow-add-points"); | ||
| } | ||
| connectedCallback() { | ||
| this.render(); | ||
| if (this.interactive) { | ||
| this.addEventListeners(); | ||
| } | ||
| } | ||
| disconnectedCallback() { | ||
| this.removeEventListeners(); | ||
| } | ||
| setPoline(poline) { | ||
| this.poline = poline; | ||
| this.updateSVG(); | ||
| this.updateLightnessBackground(); | ||
| } | ||
| setAllowAddPoints(allow) { | ||
| this.allowAddPoints = allow; | ||
| } | ||
| addPointAtPosition(x, y) { | ||
| if (!this.poline) | ||
| return null; | ||
| const normalizedX = x / this.svg.clientWidth; | ||
| const normalizedY = y / this.svg.clientHeight; | ||
| const newPoint = this.poline.addAnchorPoint({ | ||
| xyz: [normalizedX, normalizedY, normalizedY] | ||
| }); | ||
| this.updateSVG(); | ||
| this.dispatchPolineChange(); | ||
| return newPoint; | ||
| } | ||
| updateLightnessBackground() { | ||
| var _a; | ||
| const picker = (_a = this.shadowRoot) == null ? void 0 : _a.querySelector(".picker"); | ||
| if (picker && this.poline) { | ||
| if (this.poline.invertedLightness) { | ||
| picker.style.setProperty("--maxL", "#000"); | ||
| picker.style.setProperty("--minL", "#fff"); | ||
| } else { | ||
| picker.style.setProperty("--maxL", "#fff"); | ||
| picker.style.setProperty("--minL", "#000"); | ||
| } | ||
| } | ||
| } | ||
| render() { | ||
| if (!this.shadowRoot) { | ||
| return; | ||
| } | ||
| this.shadowRoot.innerHTML = ` | ||
| <style> | ||
| :host { | ||
| display: block; | ||
| width: 100%; | ||
| } | ||
| .picker { | ||
| position: relative; | ||
| width: 100%; | ||
| aspect-ratio: 1; | ||
| --wheelS: var(--poline-picker-wheel-saturation, .4); | ||
| --wheelL: var(--poline-picker-wheel-lightness, .5); | ||
| --minL: #000; | ||
| --maxL: #fff; | ||
| --grad: hsl(0deg calc(var(--wheelS) * 100%) calc(var(--wheelL) * 100%)) 0deg, hsl(60deg calc(var(--wheelS) * 100%) calc(var(--wheelL) * 100%)) 60deg, hsl(120deg calc(var(--wheelS) * 100%) calc(var(--wheelL) * 100%)) 120deg, hsl(180deg calc(var(--wheelS) * 100%) calc(var(--wheelL) * 100%)) 180deg, hsl(240deg calc(var(--wheelS) * 100%) calc(var(--wheelL) * 100%)) 240deg, hsl(300deg calc(var(--wheelS) * 100%) calc(var(--wheelL) * 100%)) 300deg, hsl(360deg calc(var(--wheelS) * 100%) calc(var(--wheelL) * 100%)) 360deg; | ||
| } | ||
| .picker::before { | ||
| content: ''; | ||
| position: absolute; | ||
| inset: 0; | ||
| border-radius: 50%; | ||
| background: radial-gradient(closest-side, var(--minL), rgba(255, 255, 255, 0), var(--maxL)), | ||
| conic-gradient(from 90deg, var(--grad)); | ||
| z-index: 1; | ||
| } | ||
| svg { | ||
| position: relative; | ||
| z-index: 2; | ||
| overflow: visible; | ||
| width: 100%; | ||
| } | ||
| .wheel__line { | ||
| stroke: var(--poline-picker-line-color, #000); | ||
| stroke-width: calc(0.75 * var(--poline-picker-line-width, 0.2)); | ||
| fill: none; | ||
| } | ||
| .wheel__anchor { | ||
| cursor: grab; | ||
| stroke: var(--poline-picker-line-color, #000); | ||
| stroke-width: var(--poline-picker-line-width, 0.2); | ||
| fill: var(--poline-picker-bg-color, #fff); | ||
| } | ||
| .wheel__anchor:hover { | ||
| cursor: grabbing; | ||
| } | ||
| .wheel__point { | ||
| stroke: var(--poline-picker-line-color, #000); | ||
| stroke-width: calc(0.75 * var(--poline-picker-line-width, 0.2)); | ||
| pointer-events: none; | ||
| } | ||
| </style> | ||
| `; | ||
| this.svg = this.createSVG(); | ||
| const pickerDiv = document.createElement("div"); | ||
| pickerDiv.className = "picker"; | ||
| pickerDiv.appendChild(this.svg); | ||
| this.shadowRoot.appendChild(pickerDiv); | ||
| this.wheel = this.svg.querySelector(".wheel"); | ||
| this.line = this.svg.querySelector(".wheel__line"); | ||
| this.anchors = this.svg.querySelector(".wheel__anchors"); | ||
| this.points = this.svg.querySelector(".wheel__points"); | ||
| if (this.poline) { | ||
| this.updateSVG(); | ||
| } | ||
| } | ||
| createSVG() { | ||
| const svg = document.createElementNS(namespaceURI, "svg"); | ||
| svg.setAttribute("viewBox", `0 0 ${svgscale} ${svgscale}`); | ||
| svg.innerHTML = ` | ||
| <defs> | ||
| <filter id="goo"> | ||
| <feGaussianBlur in="SourceGraphic" stdDeviation="1" result="blur" /> | ||
| <feColorMatrix in="blur" mode="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 18 -7" result="goo" /> | ||
| <feBlend in="SourceGraphic" in2="goo" /> | ||
| </filter> | ||
| </defs> | ||
| <g class="wheel" filter="url(#goo)"> | ||
| <polyline class="wheel__line" points="" /> | ||
| <g class="wheel__anchors"></g> | ||
| <g class="wheel__points"></g> | ||
| </g> | ||
| `; | ||
| return svg; | ||
| } | ||
| updateSVG() { | ||
| if (!this.poline || !this.svg) { | ||
| return; | ||
| } | ||
| const pathPoints = this.poline.flattenedPoints.map((p) => { | ||
| const cartesian = this.pointToCartesian(p); | ||
| if (!cartesian) | ||
| return ""; | ||
| const [x, y] = cartesian; | ||
| return `${x},${y}`; | ||
| }).filter((point) => point !== "").join(" "); | ||
| this.line.setAttribute("points", pathPoints); | ||
| this.anchors.innerHTML = ""; | ||
| this.points.innerHTML = ""; | ||
| this.poline.anchorPoints.forEach((point) => { | ||
| const anchor = this.createCircleElement(point, "wheel__anchor", "2"); | ||
| if (anchor) { | ||
| this.anchors.appendChild(anchor); | ||
| } | ||
| }); | ||
| this.poline.flattenedPoints.forEach((point) => { | ||
| const radius = 0.5 + point.color[1]; | ||
| const circle = this.createCircleElement(point, "wheel__point", radius); | ||
| if (circle) { | ||
| this.points.appendChild(circle); | ||
| } | ||
| }); | ||
| } | ||
| pointToCartesian(point) { | ||
| const half = svgscale / 2; | ||
| const x = half + (point.x - 0.5) * svgscale; | ||
| const y = half + (point.y - 0.5) * svgscale; | ||
| return [x, y]; | ||
| } | ||
| addEventListeners() { | ||
| if (!this.svg) | ||
| return; | ||
| this.svg.addEventListener("pointerdown", this.boundPointerDown); | ||
| this.svg.addEventListener("pointermove", this.boundPointerMove); | ||
| this.svg.addEventListener("pointerup", this.boundPointerUp); | ||
| } | ||
| removeEventListeners() { | ||
| if (!this.svg) | ||
| return; | ||
| this.svg.removeEventListener("pointerdown", this.boundPointerDown); | ||
| this.svg.removeEventListener("pointermove", this.boundPointerMove); | ||
| this.svg.removeEventListener("pointerup", this.boundPointerUp); | ||
| } | ||
| handlePointerDown(e) { | ||
| e.stopPropagation(); | ||
| const { normalizedX, normalizedY } = this.pointerToNormalizedCoordinates(e); | ||
| const closestAnchor = this.poline.getClosestAnchorPoint({ | ||
| xyz: [normalizedX, normalizedY, null], | ||
| maxDistance: 0.1 | ||
| }); | ||
| if (closestAnchor) { | ||
| this.currentPoint = closestAnchor; | ||
| } else if (this.allowAddPoints) { | ||
| this.currentPoint = this.poline.addAnchorPoint({ | ||
| xyz: [normalizedX, normalizedY, normalizedY] | ||
| }); | ||
| this.updateSVG(); | ||
| this.dispatchPolineChange(); | ||
| } | ||
| } | ||
| handlePointerMove(e) { | ||
| if (this.currentPoint) { | ||
| const { normalizedX, normalizedY } = this.pointerToNormalizedCoordinates(e); | ||
| this.poline.updateAnchorPoint({ | ||
| point: this.currentPoint, | ||
| xyz: [normalizedX, normalizedY, this.currentPoint.z] | ||
| }); | ||
| this.updateSVG(); | ||
| this.dispatchPolineChange(); | ||
| } | ||
| } | ||
| handlePointerUp() { | ||
| this.currentPoint = null; | ||
| } | ||
| getPointerPosition(e) { | ||
| const rect = this.svg.getBoundingClientRect(); | ||
| return { | ||
| x: e.clientX - rect.left, | ||
| y: e.clientY - rect.top | ||
| }; | ||
| } | ||
| pointerToNormalizedCoordinates(e) { | ||
| const svgRect = this.svg.getBoundingClientRect(); | ||
| const svgX = (e.clientX - svgRect.left) / svgRect.width * svgscale; | ||
| const svgY = (e.clientY - svgRect.top) / svgRect.height * svgscale; | ||
| return { | ||
| normalizedX: svgX / svgscale, | ||
| normalizedY: svgY / svgscale | ||
| }; | ||
| } | ||
| createCircleElement(point, className, radius) { | ||
| const cartesian = this.pointToCartesian(point); | ||
| if (!cartesian) | ||
| return null; | ||
| const [x = 0, y = 0] = cartesian; | ||
| const circle = document.createElementNS(namespaceURI, "circle"); | ||
| circle.setAttribute("class", className); | ||
| circle.setAttribute("cx", x.toString()); | ||
| circle.setAttribute("cy", y.toString()); | ||
| circle.setAttribute("r", radius.toString()); | ||
| circle.setAttribute("fill", point.hslCSS); | ||
| return circle; | ||
| } | ||
| dispatchPolineChange() { | ||
| this.dispatchEvent( | ||
| new CustomEvent("poline-change", { | ||
| detail: { poline: this.poline } | ||
| }) | ||
| ); | ||
| } | ||
| }; | ||
| customElements.define("poline-picker", PolinePicker); | ||
| return __toCommonJS(webcomponent_exports); | ||
| })(); |
| "use strict";var f=Object.defineProperty;var M=Object.getOwnPropertyDescriptor;var y=Object.getOwnPropertyNames;var V=Object.prototype.hasOwnProperty;var h=Math.pow;var A=(e,t)=>{for(var o in t)f(e,o,{get:t[o],enumerable:!0})},E=(e,t,o,i)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of y(t))!V.call(e,n)&&n!==o&&f(e,n,{get:()=>t[n],enumerable:!(i=M(t,n))||i.enumerable});return e};var k=e=>E(f({},"__esModule",{value:!0}),e);var I={};A(I,{Poline:()=>b,PolinePicker:()=>g,positionFunctions:()=>S});module.exports=k(I);var C=(e,t)=>{let[o,i,n]=e,s=.5,c=.5,l=Math.atan2(i-c,o-s)*(180/Math.PI);l=(360+l)%360;let a=n,d=Math.sqrt(Math.pow(i-c,2)+Math.pow(o-s,2))/s;return[l,a,t?1-d:d]},w=(e,t)=>{let[o,i,n]=e,s=.5,c=.5,r=o/(180/Math.PI),l=(t?1-n:n)*s,a=s+l*Math.cos(r),P=c+l*Math.sin(r);return[a,P,i]},_=(e=Math.random()*360,t=[Math.random(),Math.random()],o=[.75+Math.random()*.2,.3+Math.random()*.2])=>[[e,t[0],o[0]],[(e+60+Math.random()*180)%360,t[1],o[1]]];var z=(e,t,o,i=!1,n=(r,l)=>l?1-r:r,s=(r,l)=>l?1-r:r,c=(r,l)=>l?1-r:r)=>{let r=n(e,i),l=s(e,i),a=c(e,i),P=(1-r)*t[0]+r*o[0],d=(1-l)*t[1]+l*o[1],x=(1-a)*t[2]+a*o[2];return[P,d,x]},G=(e,t,o=4,i=!1,n=(r,l)=>l?1-r:r,s=(r,l)=>l?1-r:r,c=(r,l)=>l?1-r:r)=>{let r=[];for(let l=0;l<o;l++){let[a,P,d]=z(l/(o-1),e,t,i,n,s,c);r.push([a,P,d])}return r},X=e=>e,Y=(e,t=!1)=>t?1-h(1-e,2):h(e,2),T=(e,t=!1)=>t?1-h(1-e,3):h(e,3),$=(e,t=!1)=>t?1-h(1-e,4):h(e,4),Z=(e,t=!1)=>t?1-h(1-e,5):h(e,5),u=(e,t=!1)=>t?1-Math.sin((1-e)*Math.PI/2):Math.sin(e*Math.PI/2),q=(e,t=!1)=>t?1-Math.asin(1-e)/(Math.PI/2):Math.asin(e)/(Math.PI/2),D=(e,t=!1)=>t?1-Math.sqrt(1-h(e,2)):1-Math.sqrt(1-e),H=e=>h(e,2)*(3-2*e),S={linearPosition:X,exponentialPosition:Y,quadraticPosition:T,cubicPosition:$,quarticPosition:Z,sinusoidalPosition:u,asinusoidalPosition:q,arcPosition:D,smoothStepPosition:H},F=(e,t,o=!1)=>{let i=e[0],n=t[0],s=0;o&&i!==null&&n!==null?(s=Math.min(Math.abs(i-n),360-Math.abs(i-n)),s=s/360):s=i===null||n===null?0:i-n;let c=s,r=e[1]===null||t[1]===null?0:t[1]-e[1],l=e[2]===null||t[2]===null?0:t[2]-e[2];return Math.sqrt(c*c+r*r+l*l)},m=class{constructor({xyz:t,color:o,invertedLightness:i=!1}={}){this.x=0;this.y=0;this.z=0;this.color=[0,0,0];this._invertedLightness=!1;this._invertedLightness=i,this.positionOrColor({xyz:t,color:o,invertedLightness:i})}positionOrColor({xyz:t,color:o,invertedLightness:i=!1}){if(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=C([this.x,this.y,this.z],i)):o&&(this.color=o,[this.x,this.y,this.z]=w(o,i))}set position([t,o,i]){this.x=t,this.y=o,this.z=i,this.color=C([this.x,this.y,this.z],this._invertedLightness)}get position(){return[this.x,this.y,this.z]}set hsl([t,o,i]){this.color=[t,o,i],[this.x,this.y,this.z]=w(this.color,this._invertedLightness)}get hsl(){return this.color}get hslCSS(){let[t,o,i]=this.color;return`hsl(${t.toFixed(2)}, ${(o*100).toFixed(2)}%, ${(i*100).toFixed(2)}%)`}get oklchCSS(){let[t,o,i]=this.color;return`oklch(${(i*100).toFixed(2)}% ${(o*.4).toFixed(3)} ${t.toFixed(2)})`}get lchCSS(){let[t,o,i]=this.color;return`lch(${(i*100).toFixed(2)}% ${(o*150).toFixed(2)} ${t.toFixed(2)})`}shiftHue(t){this.color[0]=(360+(this.color[0]+t))%360,[this.x,this.y,this.z]=w(this.color,this._invertedLightness)}},b=class{constructor({anchorColors:t=_(),numPoints:o=4,positionFunction:i=u,positionFunctionX:n,positionFunctionY:s,positionFunctionZ:c,closedLoop:r,invertedLightness:l}={anchorColors:_(),numPoints:4,positionFunction:u,closedLoop:!1}){this._needsUpdate=!0;this._positionFunctionX=u;this._positionFunctionY=u;this._positionFunctionZ=u;this.connectLastAndFirstAnchor=!1;this._animationFrame=null;this._invertedLightness=!1;if(!t||t.length<2)throw new Error("Must have at least two anchor colors");this._anchorPoints=t.map(a=>new m({color:a,invertedLightness:l})),this._numPoints=o+2,this._positionFunctionX=n||i||u,this._positionFunctionY=s||i||u,this._positionFunctionZ=c||i||u,this.connectLastAndFirstAnchor=r||!1,this._invertedLightness=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 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<t;o++){let i=[this.anchorPoints[o],this.anchorPoints[(o+1)%this.anchorPoints.length]];this._anchorPairs.push(i)}this.points=this._anchorPairs.map((o,i)=>{let n=o[0]?o[0].position:[0,0,0],s=o[1]?o[1].position:[0,0,0];return G(n,s,this._numPoints,!!(i%2),this.positionFunctionX,this.positionFunctionY,this.positionFunctionZ).map(c=>new m({xyz:c,invertedLightness:this._invertedLightness}))})}addAnchorPoint({xyz:t,color:o,insertAtIndex:i}){let n=new m({xyz:t,color:o,invertedLightness:this._invertedLightness});return i!==void 0?this.anchorPoints.splice(i,0,n):this.anchorPoints.push(n),this.updateAnchorPairs(),n}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 i;if(o!==void 0?i=o:t&&(i=this.anchorPoints.indexOf(t)),i>-1&&i<this.anchorPoints.length)this.anchorPoints.splice(i,1),this.updateAnchorPairs();else throw new Error("Point not found")}updateAnchorPoint({point:t,pointIndex:o,xyz:i,color:n}){if(o!==void 0&&(t=this.anchorPoints[o]),!t)throw new Error("Must provide a point or pointIndex");if(!i&&!n)throw new Error("Must provide a new xyz position or color");return i&&(t.position=i),n&&(t.hsl=n),this.updateAnchorPairs(),t}getClosestAnchorPoint({xyz:t,hsl:o,maxDistance:i=1}){if(!t&&!o)throw new Error("Must provide a xyz or hsl");let n;t?n=this.anchorPoints.map(r=>F(r.position,t)):o&&(n=this.anchorPoints.map(r=>F(r.hsl,o,!0)));let s=Math.min(...n);if(s>i)return null;let c=n.indexOf(s);return this.anchorPoints[c]||null}set closedLoop(t){this.connectLastAndFirstAnchor=t,this.updateAnchorPairs()}get closedLoop(){return this.connectLastAndFirstAnchor}set invertedLightness(t){this._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&&t.pop(),t}cssColors(t="hsl"){let o={hsl:n=>n.hslCSS,oklch:n=>n.oklchCSS,lch:n=>n.lchCSS},i=this.flattenedPoints.map(o[t]);return this.connectLastAndFirstAnchor&&i.pop(),i}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()}},{p5:v}=globalThis;if(v){console.info("p5 detected, adding poline to p5 prototype");let e=new b;v.prototype.poline=e;let t=()=>e.colors.map(o=>`hsl(${Math.round(o[0])},${o[1]*100}%,${o[2]*100}%)`);v.prototype.polineColors=t,v.prototype.registerMethod("polineColors",v.prototype.polineColors),globalThis.poline=e,globalThis.polineColors=t}var L="http://www.w3.org/2000/svg",p=100,g=class extends HTMLElement{constructor(){super();this.currentPoint=null;this.allowAddPoints=!1;this.boundPointerDown=this.handlePointerDown.bind(this);this.boundPointerMove=this.handlePointerMove.bind(this);this.boundPointerUp=this.handlePointerUp.bind(this);this.attachShadow({mode:"open"}),this.interactive=this.hasAttribute("interactive"),this.allowAddPoints=this.hasAttribute("allow-add-points")}connectedCallback(){this.render(),this.interactive&&this.addEventListeners()}disconnectedCallback(){this.removeEventListeners()}setPoline(o){this.poline=o,this.updateSVG(),this.updateLightnessBackground()}setAllowAddPoints(o){this.allowAddPoints=o}addPointAtPosition(o,i){if(!this.poline)return null;let n=o/this.svg.clientWidth,s=i/this.svg.clientHeight,c=this.poline.addAnchorPoint({xyz:[n,s,s]});return this.updateSVG(),this.dispatchPolineChange(),c}updateLightnessBackground(){var i;let o=(i=this.shadowRoot)==null?void 0:i.querySelector(".picker");o&&this.poline&&(this.poline.invertedLightness?(o.style.setProperty("--maxL","#000"),o.style.setProperty("--minL","#fff")):(o.style.setProperty("--maxL","#fff"),o.style.setProperty("--minL","#000")))}render(){if(!this.shadowRoot)return;this.shadowRoot.innerHTML=` | ||
| <style> | ||
| :host { | ||
| display: block; | ||
| width: 100%; | ||
| } | ||
| .picker { | ||
| position: relative; | ||
| width: 100%; | ||
| aspect-ratio: 1; | ||
| --wheelS: var(--poline-picker-wheel-saturation, .4); | ||
| --wheelL: var(--poline-picker-wheel-lightness, .5); | ||
| --minL: #000; | ||
| --maxL: #fff; | ||
| --grad: hsl(0deg calc(var(--wheelS) * 100%) calc(var(--wheelL) * 100%)) 0deg, hsl(60deg calc(var(--wheelS) * 100%) calc(var(--wheelL) * 100%)) 60deg, hsl(120deg calc(var(--wheelS) * 100%) calc(var(--wheelL) * 100%)) 120deg, hsl(180deg calc(var(--wheelS) * 100%) calc(var(--wheelL) * 100%)) 180deg, hsl(240deg calc(var(--wheelS) * 100%) calc(var(--wheelL) * 100%)) 240deg, hsl(300deg calc(var(--wheelS) * 100%) calc(var(--wheelL) * 100%)) 300deg, hsl(360deg calc(var(--wheelS) * 100%) calc(var(--wheelL) * 100%)) 360deg; | ||
| } | ||
| .picker::before { | ||
| content: ''; | ||
| position: absolute; | ||
| inset: 0; | ||
| border-radius: 50%; | ||
| background: radial-gradient(closest-side, var(--minL), rgba(255, 255, 255, 0), var(--maxL)), | ||
| conic-gradient(from 90deg, var(--grad)); | ||
| z-index: 1; | ||
| } | ||
| svg { | ||
| position: relative; | ||
| z-index: 2; | ||
| overflow: visible; | ||
| width: 100%; | ||
| } | ||
| .wheel__line { | ||
| stroke: var(--poline-picker-line-color, #000); | ||
| stroke-width: calc(0.75 * var(--poline-picker-line-width, 0.2)); | ||
| fill: none; | ||
| } | ||
| .wheel__anchor { | ||
| cursor: grab; | ||
| stroke: var(--poline-picker-line-color, #000); | ||
| stroke-width: var(--poline-picker-line-width, 0.2); | ||
| fill: var(--poline-picker-bg-color, #fff); | ||
| } | ||
| .wheel__anchor:hover { | ||
| cursor: grabbing; | ||
| } | ||
| .wheel__point { | ||
| stroke: var(--poline-picker-line-color, #000); | ||
| stroke-width: calc(0.75 * var(--poline-picker-line-width, 0.2)); | ||
| pointer-events: none; | ||
| } | ||
| </style> | ||
| `,this.svg=this.createSVG();let o=document.createElement("div");o.className="picker",o.appendChild(this.svg),this.shadowRoot.appendChild(o),this.wheel=this.svg.querySelector(".wheel"),this.line=this.svg.querySelector(".wheel__line"),this.anchors=this.svg.querySelector(".wheel__anchors"),this.points=this.svg.querySelector(".wheel__points"),this.poline&&this.updateSVG()}createSVG(){let o=document.createElementNS(L,"svg");return o.setAttribute("viewBox",`0 0 ${p} ${p}`),o.innerHTML=` | ||
| <defs> | ||
| <filter id="goo"> | ||
| <feGaussianBlur in="SourceGraphic" stdDeviation="1" result="blur" /> | ||
| <feColorMatrix in="blur" mode="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 18 -7" result="goo" /> | ||
| <feBlend in="SourceGraphic" in2="goo" /> | ||
| </filter> | ||
| </defs> | ||
| <g class="wheel" filter="url(#goo)"> | ||
| <polyline class="wheel__line" points="" /> | ||
| <g class="wheel__anchors"></g> | ||
| <g class="wheel__points"></g> | ||
| </g> | ||
| `,o}updateSVG(){if(!this.poline||!this.svg)return;let o=this.poline.flattenedPoints.map(i=>{let n=this.pointToCartesian(i);if(!n)return"";let[s,c]=n;return`${s},${c}`}).filter(i=>i!=="").join(" ");this.line.setAttribute("points",o),this.anchors.innerHTML="",this.points.innerHTML="",this.poline.anchorPoints.forEach(i=>{let n=this.createCircleElement(i,"wheel__anchor","2");n&&this.anchors.appendChild(n)}),this.poline.flattenedPoints.forEach(i=>{let n=.5+i.color[1],s=this.createCircleElement(i,"wheel__point",n);s&&this.points.appendChild(s)})}pointToCartesian(o){let i=p/2,n=i+(o.x-.5)*p,s=i+(o.y-.5)*p;return[n,s]}addEventListeners(){this.svg&&(this.svg.addEventListener("pointerdown",this.boundPointerDown),this.svg.addEventListener("pointermove",this.boundPointerMove),this.svg.addEventListener("pointerup",this.boundPointerUp))}removeEventListeners(){this.svg&&(this.svg.removeEventListener("pointerdown",this.boundPointerDown),this.svg.removeEventListener("pointermove",this.boundPointerMove),this.svg.removeEventListener("pointerup",this.boundPointerUp))}handlePointerDown(o){o.stopPropagation();let{normalizedX:i,normalizedY:n}=this.pointerToNormalizedCoordinates(o),s=this.poline.getClosestAnchorPoint({xyz:[i,n,null],maxDistance:.1});s?this.currentPoint=s:this.allowAddPoints&&(this.currentPoint=this.poline.addAnchorPoint({xyz:[i,n,n]}),this.updateSVG(),this.dispatchPolineChange())}handlePointerMove(o){if(this.currentPoint){let{normalizedX:i,normalizedY:n}=this.pointerToNormalizedCoordinates(o);this.poline.updateAnchorPoint({point:this.currentPoint,xyz:[i,n,this.currentPoint.z]}),this.updateSVG(),this.dispatchPolineChange()}}handlePointerUp(){this.currentPoint=null}getPointerPosition(o){let i=this.svg.getBoundingClientRect();return{x:o.clientX-i.left,y:o.clientY-i.top}}pointerToNormalizedCoordinates(o){let i=this.svg.getBoundingClientRect(),n=(o.clientX-i.left)/i.width*p,s=(o.clientY-i.top)/i.height*p;return{normalizedX:n/p,normalizedY:s/p}}createCircleElement(o,i,n){let s=this.pointToCartesian(o);if(!s)return null;let[c=0,r=0]=s,l=document.createElementNS(L,"circle");return l.setAttribute("class",i),l.setAttribute("cx",c.toString()),l.setAttribute("cy",r.toString()),l.setAttribute("r",n.toString()),l.setAttribute("fill",o.hslCSS),l}dispatchPolineChange(){this.dispatchEvent(new CustomEvent("poline-change",{detail:{poline:this.poline}}))}};customElements.define("poline-picker",g); |
| "use strict";var polinePicker=(()=>{var f=Object.defineProperty;var M=Object.getOwnPropertyDescriptor;var y=Object.getOwnPropertyNames;var V=Object.prototype.hasOwnProperty;var h=Math.pow;var A=(e,t)=>{for(var o in t)f(e,o,{get:t[o],enumerable:!0})},E=(e,t,o,i)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of y(t))!V.call(e,n)&&n!==o&&f(e,n,{get:()=>t[n],enumerable:!(i=M(t,n))||i.enumerable});return e};var k=e=>E(f({},"__esModule",{value:!0}),e);var I={};A(I,{Poline:()=>b,PolinePicker:()=>g,positionFunctions:()=>S});var C=(e,t)=>{let[o,i,n]=e,s=.5,c=.5,l=Math.atan2(i-c,o-s)*(180/Math.PI);l=(360+l)%360;let a=n,d=Math.sqrt(Math.pow(i-c,2)+Math.pow(o-s,2))/s;return[l,a,t?1-d:d]},w=(e,t)=>{let[o,i,n]=e,s=.5,c=.5,r=o/(180/Math.PI),l=(t?1-n:n)*s,a=s+l*Math.cos(r),P=c+l*Math.sin(r);return[a,P,i]},_=(e=Math.random()*360,t=[Math.random(),Math.random()],o=[.75+Math.random()*.2,.3+Math.random()*.2])=>[[e,t[0],o[0]],[(e+60+Math.random()*180)%360,t[1],o[1]]];var z=(e,t,o,i=!1,n=(r,l)=>l?1-r:r,s=(r,l)=>l?1-r:r,c=(r,l)=>l?1-r:r)=>{let r=n(e,i),l=s(e,i),a=c(e,i),P=(1-r)*t[0]+r*o[0],d=(1-l)*t[1]+l*o[1],x=(1-a)*t[2]+a*o[2];return[P,d,x]},G=(e,t,o=4,i=!1,n=(r,l)=>l?1-r:r,s=(r,l)=>l?1-r:r,c=(r,l)=>l?1-r:r)=>{let r=[];for(let l=0;l<o;l++){let[a,P,d]=z(l/(o-1),e,t,i,n,s,c);r.push([a,P,d])}return r},X=e=>e,Y=(e,t=!1)=>t?1-h(1-e,2):h(e,2),T=(e,t=!1)=>t?1-h(1-e,3):h(e,3),$=(e,t=!1)=>t?1-h(1-e,4):h(e,4),Z=(e,t=!1)=>t?1-h(1-e,5):h(e,5),u=(e,t=!1)=>t?1-Math.sin((1-e)*Math.PI/2):Math.sin(e*Math.PI/2),q=(e,t=!1)=>t?1-Math.asin(1-e)/(Math.PI/2):Math.asin(e)/(Math.PI/2),D=(e,t=!1)=>t?1-Math.sqrt(1-h(e,2)):1-Math.sqrt(1-e),H=e=>h(e,2)*(3-2*e),S={linearPosition:X,exponentialPosition:Y,quadraticPosition:T,cubicPosition:$,quarticPosition:Z,sinusoidalPosition:u,asinusoidalPosition:q,arcPosition:D,smoothStepPosition:H},F=(e,t,o=!1)=>{let i=e[0],n=t[0],s=0;o&&i!==null&&n!==null?(s=Math.min(Math.abs(i-n),360-Math.abs(i-n)),s=s/360):s=i===null||n===null?0:i-n;let c=s,r=e[1]===null||t[1]===null?0:t[1]-e[1],l=e[2]===null||t[2]===null?0:t[2]-e[2];return Math.sqrt(c*c+r*r+l*l)},m=class{constructor({xyz:t,color:o,invertedLightness:i=!1}={}){this.x=0;this.y=0;this.z=0;this.color=[0,0,0];this._invertedLightness=!1;this._invertedLightness=i,this.positionOrColor({xyz:t,color:o,invertedLightness:i})}positionOrColor({xyz:t,color:o,invertedLightness:i=!1}){if(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=C([this.x,this.y,this.z],i)):o&&(this.color=o,[this.x,this.y,this.z]=w(o,i))}set position([t,o,i]){this.x=t,this.y=o,this.z=i,this.color=C([this.x,this.y,this.z],this._invertedLightness)}get position(){return[this.x,this.y,this.z]}set hsl([t,o,i]){this.color=[t,o,i],[this.x,this.y,this.z]=w(this.color,this._invertedLightness)}get hsl(){return this.color}get hslCSS(){let[t,o,i]=this.color;return`hsl(${t.toFixed(2)}, ${(o*100).toFixed(2)}%, ${(i*100).toFixed(2)}%)`}get oklchCSS(){let[t,o,i]=this.color;return`oklch(${(i*100).toFixed(2)}% ${(o*.4).toFixed(3)} ${t.toFixed(2)})`}get lchCSS(){let[t,o,i]=this.color;return`lch(${(i*100).toFixed(2)}% ${(o*150).toFixed(2)} ${t.toFixed(2)})`}shiftHue(t){this.color[0]=(360+(this.color[0]+t))%360,[this.x,this.y,this.z]=w(this.color,this._invertedLightness)}},b=class{constructor({anchorColors:t=_(),numPoints:o=4,positionFunction:i=u,positionFunctionX:n,positionFunctionY:s,positionFunctionZ:c,closedLoop:r,invertedLightness:l}={anchorColors:_(),numPoints:4,positionFunction:u,closedLoop:!1}){this._needsUpdate=!0;this._positionFunctionX=u;this._positionFunctionY=u;this._positionFunctionZ=u;this.connectLastAndFirstAnchor=!1;this._animationFrame=null;this._invertedLightness=!1;if(!t||t.length<2)throw new Error("Must have at least two anchor colors");this._anchorPoints=t.map(a=>new m({color:a,invertedLightness:l})),this._numPoints=o+2,this._positionFunctionX=n||i||u,this._positionFunctionY=s||i||u,this._positionFunctionZ=c||i||u,this.connectLastAndFirstAnchor=r||!1,this._invertedLightness=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 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<t;o++){let i=[this.anchorPoints[o],this.anchorPoints[(o+1)%this.anchorPoints.length]];this._anchorPairs.push(i)}this.points=this._anchorPairs.map((o,i)=>{let n=o[0]?o[0].position:[0,0,0],s=o[1]?o[1].position:[0,0,0];return G(n,s,this._numPoints,!!(i%2),this.positionFunctionX,this.positionFunctionY,this.positionFunctionZ).map(c=>new m({xyz:c,invertedLightness:this._invertedLightness}))})}addAnchorPoint({xyz:t,color:o,insertAtIndex:i}){let n=new m({xyz:t,color:o,invertedLightness:this._invertedLightness});return i!==void 0?this.anchorPoints.splice(i,0,n):this.anchorPoints.push(n),this.updateAnchorPairs(),n}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 i;if(o!==void 0?i=o:t&&(i=this.anchorPoints.indexOf(t)),i>-1&&i<this.anchorPoints.length)this.anchorPoints.splice(i,1),this.updateAnchorPairs();else throw new Error("Point not found")}updateAnchorPoint({point:t,pointIndex:o,xyz:i,color:n}){if(o!==void 0&&(t=this.anchorPoints[o]),!t)throw new Error("Must provide a point or pointIndex");if(!i&&!n)throw new Error("Must provide a new xyz position or color");return i&&(t.position=i),n&&(t.hsl=n),this.updateAnchorPairs(),t}getClosestAnchorPoint({xyz:t,hsl:o,maxDistance:i=1}){if(!t&&!o)throw new Error("Must provide a xyz or hsl");let n;t?n=this.anchorPoints.map(r=>F(r.position,t)):o&&(n=this.anchorPoints.map(r=>F(r.hsl,o,!0)));let s=Math.min(...n);if(s>i)return null;let c=n.indexOf(s);return this.anchorPoints[c]||null}set closedLoop(t){this.connectLastAndFirstAnchor=t,this.updateAnchorPairs()}get closedLoop(){return this.connectLastAndFirstAnchor}set invertedLightness(t){this._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&&t.pop(),t}cssColors(t="hsl"){let o={hsl:n=>n.hslCSS,oklch:n=>n.oklchCSS,lch:n=>n.lchCSS},i=this.flattenedPoints.map(o[t]);return this.connectLastAndFirstAnchor&&i.pop(),i}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()}},{p5:v}=globalThis;if(v){console.info("p5 detected, adding poline to p5 prototype");let e=new b;v.prototype.poline=e;let t=()=>e.colors.map(o=>`hsl(${Math.round(o[0])},${o[1]*100}%,${o[2]*100}%)`);v.prototype.polineColors=t,v.prototype.registerMethod("polineColors",v.prototype.polineColors),globalThis.poline=e,globalThis.polineColors=t}var L="http://www.w3.org/2000/svg",p=100,g=class extends HTMLElement{constructor(){super();this.currentPoint=null;this.allowAddPoints=!1;this.boundPointerDown=this.handlePointerDown.bind(this);this.boundPointerMove=this.handlePointerMove.bind(this);this.boundPointerUp=this.handlePointerUp.bind(this);this.attachShadow({mode:"open"}),this.interactive=this.hasAttribute("interactive"),this.allowAddPoints=this.hasAttribute("allow-add-points")}connectedCallback(){this.render(),this.interactive&&this.addEventListeners()}disconnectedCallback(){this.removeEventListeners()}setPoline(o){this.poline=o,this.updateSVG(),this.updateLightnessBackground()}setAllowAddPoints(o){this.allowAddPoints=o}addPointAtPosition(o,i){if(!this.poline)return null;let n=o/this.svg.clientWidth,s=i/this.svg.clientHeight,c=this.poline.addAnchorPoint({xyz:[n,s,s]});return this.updateSVG(),this.dispatchPolineChange(),c}updateLightnessBackground(){var i;let o=(i=this.shadowRoot)==null?void 0:i.querySelector(".picker");o&&this.poline&&(this.poline.invertedLightness?(o.style.setProperty("--maxL","#000"),o.style.setProperty("--minL","#fff")):(o.style.setProperty("--maxL","#fff"),o.style.setProperty("--minL","#000")))}render(){if(!this.shadowRoot)return;this.shadowRoot.innerHTML=` | ||
| <style> | ||
| :host { | ||
| display: block; | ||
| width: 100%; | ||
| } | ||
| .picker { | ||
| position: relative; | ||
| width: 100%; | ||
| aspect-ratio: 1; | ||
| --wheelS: var(--poline-picker-wheel-saturation, .4); | ||
| --wheelL: var(--poline-picker-wheel-lightness, .5); | ||
| --minL: #000; | ||
| --maxL: #fff; | ||
| --grad: hsl(0deg calc(var(--wheelS) * 100%) calc(var(--wheelL) * 100%)) 0deg, hsl(60deg calc(var(--wheelS) * 100%) calc(var(--wheelL) * 100%)) 60deg, hsl(120deg calc(var(--wheelS) * 100%) calc(var(--wheelL) * 100%)) 120deg, hsl(180deg calc(var(--wheelS) * 100%) calc(var(--wheelL) * 100%)) 180deg, hsl(240deg calc(var(--wheelS) * 100%) calc(var(--wheelL) * 100%)) 240deg, hsl(300deg calc(var(--wheelS) * 100%) calc(var(--wheelL) * 100%)) 300deg, hsl(360deg calc(var(--wheelS) * 100%) calc(var(--wheelL) * 100%)) 360deg; | ||
| } | ||
| .picker::before { | ||
| content: ''; | ||
| position: absolute; | ||
| inset: 0; | ||
| border-radius: 50%; | ||
| background: radial-gradient(closest-side, var(--minL), rgba(255, 255, 255, 0), var(--maxL)), | ||
| conic-gradient(from 90deg, var(--grad)); | ||
| z-index: 1; | ||
| } | ||
| svg { | ||
| position: relative; | ||
| z-index: 2; | ||
| overflow: visible; | ||
| width: 100%; | ||
| } | ||
| .wheel__line { | ||
| stroke: var(--poline-picker-line-color, #000); | ||
| stroke-width: calc(0.75 * var(--poline-picker-line-width, 0.2)); | ||
| fill: none; | ||
| } | ||
| .wheel__anchor { | ||
| cursor: grab; | ||
| stroke: var(--poline-picker-line-color, #000); | ||
| stroke-width: var(--poline-picker-line-width, 0.2); | ||
| fill: var(--poline-picker-bg-color, #fff); | ||
| } | ||
| .wheel__anchor:hover { | ||
| cursor: grabbing; | ||
| } | ||
| .wheel__point { | ||
| stroke: var(--poline-picker-line-color, #000); | ||
| stroke-width: calc(0.75 * var(--poline-picker-line-width, 0.2)); | ||
| pointer-events: none; | ||
| } | ||
| </style> | ||
| `,this.svg=this.createSVG();let o=document.createElement("div");o.className="picker",o.appendChild(this.svg),this.shadowRoot.appendChild(o),this.wheel=this.svg.querySelector(".wheel"),this.line=this.svg.querySelector(".wheel__line"),this.anchors=this.svg.querySelector(".wheel__anchors"),this.points=this.svg.querySelector(".wheel__points"),this.poline&&this.updateSVG()}createSVG(){let o=document.createElementNS(L,"svg");return o.setAttribute("viewBox",`0 0 ${p} ${p}`),o.innerHTML=` | ||
| <defs> | ||
| <filter id="goo"> | ||
| <feGaussianBlur in="SourceGraphic" stdDeviation="1" result="blur" /> | ||
| <feColorMatrix in="blur" mode="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 18 -7" result="goo" /> | ||
| <feBlend in="SourceGraphic" in2="goo" /> | ||
| </filter> | ||
| </defs> | ||
| <g class="wheel" filter="url(#goo)"> | ||
| <polyline class="wheel__line" points="" /> | ||
| <g class="wheel__anchors"></g> | ||
| <g class="wheel__points"></g> | ||
| </g> | ||
| `,o}updateSVG(){if(!this.poline||!this.svg)return;let o=this.poline.flattenedPoints.map(i=>{let n=this.pointToCartesian(i);if(!n)return"";let[s,c]=n;return`${s},${c}`}).filter(i=>i!=="").join(" ");this.line.setAttribute("points",o),this.anchors.innerHTML="",this.points.innerHTML="",this.poline.anchorPoints.forEach(i=>{let n=this.createCircleElement(i,"wheel__anchor","2");n&&this.anchors.appendChild(n)}),this.poline.flattenedPoints.forEach(i=>{let n=.5+i.color[1],s=this.createCircleElement(i,"wheel__point",n);s&&this.points.appendChild(s)})}pointToCartesian(o){let i=p/2,n=i+(o.x-.5)*p,s=i+(o.y-.5)*p;return[n,s]}addEventListeners(){this.svg&&(this.svg.addEventListener("pointerdown",this.boundPointerDown),this.svg.addEventListener("pointermove",this.boundPointerMove),this.svg.addEventListener("pointerup",this.boundPointerUp))}removeEventListeners(){this.svg&&(this.svg.removeEventListener("pointerdown",this.boundPointerDown),this.svg.removeEventListener("pointermove",this.boundPointerMove),this.svg.removeEventListener("pointerup",this.boundPointerUp))}handlePointerDown(o){o.stopPropagation();let{normalizedX:i,normalizedY:n}=this.pointerToNormalizedCoordinates(o),s=this.poline.getClosestAnchorPoint({xyz:[i,n,null],maxDistance:.1});s?this.currentPoint=s:this.allowAddPoints&&(this.currentPoint=this.poline.addAnchorPoint({xyz:[i,n,n]}),this.updateSVG(),this.dispatchPolineChange())}handlePointerMove(o){if(this.currentPoint){let{normalizedX:i,normalizedY:n}=this.pointerToNormalizedCoordinates(o);this.poline.updateAnchorPoint({point:this.currentPoint,xyz:[i,n,this.currentPoint.z]}),this.updateSVG(),this.dispatchPolineChange()}}handlePointerUp(){this.currentPoint=null}getPointerPosition(o){let i=this.svg.getBoundingClientRect();return{x:o.clientX-i.left,y:o.clientY-i.top}}pointerToNormalizedCoordinates(o){let i=this.svg.getBoundingClientRect(),n=(o.clientX-i.left)/i.width*p,s=(o.clientY-i.top)/i.height*p;return{normalizedX:n/p,normalizedY:s/p}}createCircleElement(o,i,n){let s=this.pointToCartesian(o);if(!s)return null;let[c=0,r=0]=s,l=document.createElementNS(L,"circle");return l.setAttribute("class",i),l.setAttribute("cx",c.toString()),l.setAttribute("cy",r.toString()),l.setAttribute("r",n.toString()),l.setAttribute("fill",o.hslCSS),l}dispatchPolineChange(){this.dispatchEvent(new CustomEvent("poline-change",{detail:{poline:this.poline}}))}};customElements.define("poline-picker",g);return k(I);})(); |
| var f=(e,t)=>{let[o,i,n]=e,s=.5,c=.5,l=Math.atan2(i-c,o-s)*(180/Math.PI);l=(360+l)%360;let h=n,p=Math.sqrt(Math.pow(i-c,2)+Math.pow(o-s,2))/s;return[l,h,t?1-p:p]},b=(e,t)=>{let[o,i,n]=e,s=.5,c=.5,r=o/(180/Math.PI),l=(t?1-n:n)*s,h=s+l*Math.cos(r),d=c+l*Math.sin(r);return[h,d,i]},w=(e=Math.random()*360,t=[Math.random(),Math.random()],o=[.75+Math.random()*.2,.3+Math.random()*.2])=>[[e,t[0],o[0]],[(e+60+Math.random()*180)%360,t[1],o[1]]];var S=(e,t,o,i=!1,n=(r,l)=>l?1-r:r,s=(r,l)=>l?1-r:r,c=(r,l)=>l?1-r:r)=>{let r=n(e,i),l=s(e,i),h=c(e,i),d=(1-r)*t[0]+r*o[0],p=(1-l)*t[1]+l*o[1],F=(1-h)*t[2]+h*o[2];return[d,p,F]},L=(e,t,o=4,i=!1,n=(r,l)=>l?1-r:r,s=(r,l)=>l?1-r:r,c=(r,l)=>l?1-r:r)=>{let r=[];for(let l=0;l<o;l++){let[h,d,p]=S(l/(o-1),e,t,i,n,s,c);r.push([h,d,p])}return r},x=e=>e,M=(e,t=!1)=>t?1-(1-e)**2:e**2,y=(e,t=!1)=>t?1-(1-e)**3:e**3,V=(e,t=!1)=>t?1-(1-e)**4:e**4,A=(e,t=!1)=>t?1-(1-e)**5:e**5,a=(e,t=!1)=>t?1-Math.sin((1-e)*Math.PI/2):Math.sin(e*Math.PI/2),E=(e,t=!1)=>t?1-Math.asin(1-e)/(Math.PI/2):Math.asin(e)/(Math.PI/2),k=(e,t=!1)=>t?1-Math.sqrt(1-e**2):1-Math.sqrt(1-e),z=e=>e**2*(3-2*e),G={linearPosition:x,exponentialPosition:M,quadraticPosition:y,cubicPosition:V,quarticPosition:A,sinusoidalPosition:a,asinusoidalPosition:E,arcPosition:k,smoothStepPosition:z},C=(e,t,o=!1)=>{let i=e[0],n=t[0],s=0;o&&i!==null&&n!==null?(s=Math.min(Math.abs(i-n),360-Math.abs(i-n)),s=s/360):s=i===null||n===null?0:i-n;let c=s,r=e[1]===null||t[1]===null?0:t[1]-e[1],l=e[2]===null||t[2]===null?0:t[2]-e[2];return Math.sqrt(c*c+r*r+l*l)},v=class{constructor({xyz:t,color:o,invertedLightness:i=!1}={}){this.x=0;this.y=0;this.z=0;this.color=[0,0,0];this._invertedLightness=!1;this._invertedLightness=i,this.positionOrColor({xyz:t,color:o,invertedLightness:i})}positionOrColor({xyz:t,color:o,invertedLightness:i=!1}){if(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=f([this.x,this.y,this.z],i)):o&&(this.color=o,[this.x,this.y,this.z]=b(o,i))}set position([t,o,i]){this.x=t,this.y=o,this.z=i,this.color=f([this.x,this.y,this.z],this._invertedLightness)}get position(){return[this.x,this.y,this.z]}set hsl([t,o,i]){this.color=[t,o,i],[this.x,this.y,this.z]=b(this.color,this._invertedLightness)}get hsl(){return this.color}get hslCSS(){let[t,o,i]=this.color;return`hsl(${t.toFixed(2)}, ${(o*100).toFixed(2)}%, ${(i*100).toFixed(2)}%)`}get oklchCSS(){let[t,o,i]=this.color;return`oklch(${(i*100).toFixed(2)}% ${(o*.4).toFixed(3)} ${t.toFixed(2)})`}get lchCSS(){let[t,o,i]=this.color;return`lch(${(i*100).toFixed(2)}% ${(o*150).toFixed(2)} ${t.toFixed(2)})`}shiftHue(t){this.color[0]=(360+(this.color[0]+t))%360,[this.x,this.y,this.z]=b(this.color,this._invertedLightness)}},m=class{constructor({anchorColors:t=w(),numPoints:o=4,positionFunction:i=a,positionFunctionX:n,positionFunctionY:s,positionFunctionZ:c,closedLoop:r,invertedLightness:l}={anchorColors:w(),numPoints:4,positionFunction:a,closedLoop:!1}){this._needsUpdate=!0;this._positionFunctionX=a;this._positionFunctionY=a;this._positionFunctionZ=a;this.connectLastAndFirstAnchor=!1;this._animationFrame=null;this._invertedLightness=!1;if(!t||t.length<2)throw new Error("Must have at least two anchor colors");this._anchorPoints=t.map(h=>new v({color:h,invertedLightness:l})),this._numPoints=o+2,this._positionFunctionX=n||i||a,this._positionFunctionY=s||i||a,this._positionFunctionZ=c||i||a,this.connectLastAndFirstAnchor=r||!1,this._invertedLightness=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 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<t;o++){let i=[this.anchorPoints[o],this.anchorPoints[(o+1)%this.anchorPoints.length]];this._anchorPairs.push(i)}this.points=this._anchorPairs.map((o,i)=>{let n=o[0]?o[0].position:[0,0,0],s=o[1]?o[1].position:[0,0,0];return L(n,s,this._numPoints,!!(i%2),this.positionFunctionX,this.positionFunctionY,this.positionFunctionZ).map(c=>new v({xyz:c,invertedLightness:this._invertedLightness}))})}addAnchorPoint({xyz:t,color:o,insertAtIndex:i}){let n=new v({xyz:t,color:o,invertedLightness:this._invertedLightness});return i!==void 0?this.anchorPoints.splice(i,0,n):this.anchorPoints.push(n),this.updateAnchorPairs(),n}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 i;if(o!==void 0?i=o:t&&(i=this.anchorPoints.indexOf(t)),i>-1&&i<this.anchorPoints.length)this.anchorPoints.splice(i,1),this.updateAnchorPairs();else throw new Error("Point not found")}updateAnchorPoint({point:t,pointIndex:o,xyz:i,color:n}){if(o!==void 0&&(t=this.anchorPoints[o]),!t)throw new Error("Must provide a point or pointIndex");if(!i&&!n)throw new Error("Must provide a new xyz position or color");return i&&(t.position=i),n&&(t.hsl=n),this.updateAnchorPairs(),t}getClosestAnchorPoint({xyz:t,hsl:o,maxDistance:i=1}){if(!t&&!o)throw new Error("Must provide a xyz or hsl");let n;t?n=this.anchorPoints.map(r=>C(r.position,t)):o&&(n=this.anchorPoints.map(r=>C(r.hsl,o,!0)));let s=Math.min(...n);if(s>i)return null;let c=n.indexOf(s);return this.anchorPoints[c]||null}set closedLoop(t){this.connectLastAndFirstAnchor=t,this.updateAnchorPairs()}get closedLoop(){return this.connectLastAndFirstAnchor}set invertedLightness(t){this._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&&t.pop(),t}cssColors(t="hsl"){let o={hsl:n=>n.hslCSS,oklch:n=>n.oklchCSS,lch:n=>n.lchCSS},i=this.flattenedPoints.map(o[t]);return this.connectLastAndFirstAnchor&&i.pop(),i}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()}},{p5:P}=globalThis;if(P){console.info("p5 detected, adding poline to p5 prototype");let e=new m;P.prototype.poline=e;let t=()=>e.colors.map(o=>`hsl(${Math.round(o[0])},${o[1]*100}%,${o[2]*100}%)`);P.prototype.polineColors=t,P.prototype.registerMethod("polineColors",P.prototype.polineColors),globalThis.poline=e,globalThis.polineColors=t}var _="http://www.w3.org/2000/svg",u=100,g=class extends HTMLElement{constructor(){super();this.currentPoint=null;this.allowAddPoints=!1;this.boundPointerDown=this.handlePointerDown.bind(this);this.boundPointerMove=this.handlePointerMove.bind(this);this.boundPointerUp=this.handlePointerUp.bind(this);this.attachShadow({mode:"open"}),this.interactive=this.hasAttribute("interactive"),this.allowAddPoints=this.hasAttribute("allow-add-points")}connectedCallback(){this.render(),this.interactive&&this.addEventListeners()}disconnectedCallback(){this.removeEventListeners()}setPoline(o){this.poline=o,this.updateSVG(),this.updateLightnessBackground()}setAllowAddPoints(o){this.allowAddPoints=o}addPointAtPosition(o,i){if(!this.poline)return null;let n=o/this.svg.clientWidth,s=i/this.svg.clientHeight,c=this.poline.addAnchorPoint({xyz:[n,s,s]});return this.updateSVG(),this.dispatchPolineChange(),c}updateLightnessBackground(){let o=this.shadowRoot?.querySelector(".picker");o&&this.poline&&(this.poline.invertedLightness?(o.style.setProperty("--maxL","#000"),o.style.setProperty("--minL","#fff")):(o.style.setProperty("--maxL","#fff"),o.style.setProperty("--minL","#000")))}render(){if(!this.shadowRoot)return;this.shadowRoot.innerHTML=` | ||
| <style> | ||
| :host { | ||
| display: block; | ||
| width: 100%; | ||
| } | ||
| .picker { | ||
| position: relative; | ||
| width: 100%; | ||
| aspect-ratio: 1; | ||
| --wheelS: var(--poline-picker-wheel-saturation, .4); | ||
| --wheelL: var(--poline-picker-wheel-lightness, .5); | ||
| --minL: #000; | ||
| --maxL: #fff; | ||
| --grad: hsl(0deg calc(var(--wheelS) * 100%) calc(var(--wheelL) * 100%)) 0deg, hsl(60deg calc(var(--wheelS) * 100%) calc(var(--wheelL) * 100%)) 60deg, hsl(120deg calc(var(--wheelS) * 100%) calc(var(--wheelL) * 100%)) 120deg, hsl(180deg calc(var(--wheelS) * 100%) calc(var(--wheelL) * 100%)) 180deg, hsl(240deg calc(var(--wheelS) * 100%) calc(var(--wheelL) * 100%)) 240deg, hsl(300deg calc(var(--wheelS) * 100%) calc(var(--wheelL) * 100%)) 300deg, hsl(360deg calc(var(--wheelS) * 100%) calc(var(--wheelL) * 100%)) 360deg; | ||
| } | ||
| .picker::before { | ||
| content: ''; | ||
| position: absolute; | ||
| inset: 0; | ||
| border-radius: 50%; | ||
| background: radial-gradient(closest-side, var(--minL), rgba(255, 255, 255, 0), var(--maxL)), | ||
| conic-gradient(from 90deg, var(--grad)); | ||
| z-index: 1; | ||
| } | ||
| svg { | ||
| position: relative; | ||
| z-index: 2; | ||
| overflow: visible; | ||
| width: 100%; | ||
| } | ||
| .wheel__line { | ||
| stroke: var(--poline-picker-line-color, #000); | ||
| stroke-width: calc(0.75 * var(--poline-picker-line-width, 0.2)); | ||
| fill: none; | ||
| } | ||
| .wheel__anchor { | ||
| cursor: grab; | ||
| stroke: var(--poline-picker-line-color, #000); | ||
| stroke-width: var(--poline-picker-line-width, 0.2); | ||
| fill: var(--poline-picker-bg-color, #fff); | ||
| } | ||
| .wheel__anchor:hover { | ||
| cursor: grabbing; | ||
| } | ||
| .wheel__point { | ||
| stroke: var(--poline-picker-line-color, #000); | ||
| stroke-width: calc(0.75 * var(--poline-picker-line-width, 0.2)); | ||
| pointer-events: none; | ||
| } | ||
| </style> | ||
| `,this.svg=this.createSVG();let o=document.createElement("div");o.className="picker",o.appendChild(this.svg),this.shadowRoot.appendChild(o),this.wheel=this.svg.querySelector(".wheel"),this.line=this.svg.querySelector(".wheel__line"),this.anchors=this.svg.querySelector(".wheel__anchors"),this.points=this.svg.querySelector(".wheel__points"),this.poline&&this.updateSVG()}createSVG(){let o=document.createElementNS(_,"svg");return o.setAttribute("viewBox",`0 0 ${u} ${u}`),o.innerHTML=` | ||
| <defs> | ||
| <filter id="goo"> | ||
| <feGaussianBlur in="SourceGraphic" stdDeviation="1" result="blur" /> | ||
| <feColorMatrix in="blur" mode="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 18 -7" result="goo" /> | ||
| <feBlend in="SourceGraphic" in2="goo" /> | ||
| </filter> | ||
| </defs> | ||
| <g class="wheel" filter="url(#goo)"> | ||
| <polyline class="wheel__line" points="" /> | ||
| <g class="wheel__anchors"></g> | ||
| <g class="wheel__points"></g> | ||
| </g> | ||
| `,o}updateSVG(){if(!this.poline||!this.svg)return;let o=this.poline.flattenedPoints.map(i=>{let n=this.pointToCartesian(i);if(!n)return"";let[s,c]=n;return`${s},${c}`}).filter(i=>i!=="").join(" ");this.line.setAttribute("points",o),this.anchors.innerHTML="",this.points.innerHTML="",this.poline.anchorPoints.forEach(i=>{let n=this.createCircleElement(i,"wheel__anchor","2");n&&this.anchors.appendChild(n)}),this.poline.flattenedPoints.forEach(i=>{let n=.5+i.color[1],s=this.createCircleElement(i,"wheel__point",n);s&&this.points.appendChild(s)})}pointToCartesian(o){let i=u/2,n=i+(o.x-.5)*u,s=i+(o.y-.5)*u;return[n,s]}addEventListeners(){this.svg&&(this.svg.addEventListener("pointerdown",this.boundPointerDown),this.svg.addEventListener("pointermove",this.boundPointerMove),this.svg.addEventListener("pointerup",this.boundPointerUp))}removeEventListeners(){this.svg&&(this.svg.removeEventListener("pointerdown",this.boundPointerDown),this.svg.removeEventListener("pointermove",this.boundPointerMove),this.svg.removeEventListener("pointerup",this.boundPointerUp))}handlePointerDown(o){o.stopPropagation();let{normalizedX:i,normalizedY:n}=this.pointerToNormalizedCoordinates(o),s=this.poline.getClosestAnchorPoint({xyz:[i,n,null],maxDistance:.1});s?this.currentPoint=s:this.allowAddPoints&&(this.currentPoint=this.poline.addAnchorPoint({xyz:[i,n,n]}),this.updateSVG(),this.dispatchPolineChange())}handlePointerMove(o){if(this.currentPoint){let{normalizedX:i,normalizedY:n}=this.pointerToNormalizedCoordinates(o);this.poline.updateAnchorPoint({point:this.currentPoint,xyz:[i,n,this.currentPoint.z]}),this.updateSVG(),this.dispatchPolineChange()}}handlePointerUp(){this.currentPoint=null}getPointerPosition(o){let i=this.svg.getBoundingClientRect();return{x:o.clientX-i.left,y:o.clientY-i.top}}pointerToNormalizedCoordinates(o){let i=this.svg.getBoundingClientRect(),n=(o.clientX-i.left)/i.width*u,s=(o.clientY-i.top)/i.height*u;return{normalizedX:n/u,normalizedY:s/u}}createCircleElement(o,i,n){let s=this.pointToCartesian(o);if(!s)return null;let[c=0,r=0]=s,l=document.createElementNS(_,"circle");return l.setAttribute("class",i),l.setAttribute("cx",c.toString()),l.setAttribute("cy",r.toString()),l.setAttribute("r",n.toString()),l.setAttribute("fill",o.hslCSS),l}dispatchPolineChange(){this.dispatchEvent(new CustomEvent("poline-change",{detail:{poline:this.poline}}))}};customElements.define("poline-picker",g);export{m as Poline,g as PolinePicker,G as positionFunctions}; |
+769
| // src/index.ts | ||
| var pointToHSL = (xyz, invertedLightness) => { | ||
| const [x, y, z] = xyz; | ||
| const cx = 0.5; | ||
| const cy = 0.5; | ||
| const radians = Math.atan2(y - cy, x - cx); | ||
| let deg = radians * (180 / Math.PI); | ||
| deg = (360 + deg) % 360; | ||
| const s = z; | ||
| const dist = Math.sqrt(Math.pow(y - cy, 2) + Math.pow(x - cx, 2)); | ||
| const l = dist / cx; | ||
| return [deg, s, invertedLightness ? 1 - l : l]; | ||
| }; | ||
| var hslToPoint = (hsl, invertedLightness) => { | ||
| const [h, s, l] = hsl; | ||
| const cx = 0.5; | ||
| const cy = 0.5; | ||
| const radians = h / (180 / Math.PI); | ||
| const dist = (invertedLightness ? 1 - l : l) * cx; | ||
| const x = cx + dist * Math.cos(radians); | ||
| const y = cy + dist * Math.sin(radians); | ||
| const z = s; | ||
| return [x, y, z]; | ||
| }; | ||
| var randomHSLPair = (startHue = Math.random() * 360, saturations = [Math.random(), Math.random()], lightnesses = [0.75 + Math.random() * 0.2, 0.3 + Math.random() * 0.2]) => [ | ||
| [startHue, saturations[0], lightnesses[0]], | ||
| [(startHue + 60 + Math.random() * 180) % 360, saturations[1], lightnesses[1]] | ||
| ]; | ||
| var vectorOnLine = (t, p1, p2, invert = false, fx = (t2, invert2) => invert2 ? 1 - t2 : t2, fy = (t2, invert2) => invert2 ? 1 - t2 : t2, fz = (t2, invert2) => invert2 ? 1 - t2 : t2) => { | ||
| const tModifiedX = fx(t, invert); | ||
| const tModifiedY = fy(t, invert); | ||
| const tModifiedZ = fz(t, invert); | ||
| const x = (1 - tModifiedX) * p1[0] + tModifiedX * p2[0]; | ||
| const y = (1 - tModifiedY) * p1[1] + tModifiedY * p2[1]; | ||
| const z = (1 - tModifiedZ) * p1[2] + tModifiedZ * p2[2]; | ||
| return [x, y, z]; | ||
| }; | ||
| var vectorsOnLine = (p1, p2, numPoints = 4, invert = false, fx = (t, invert2) => invert2 ? 1 - t : t, fy = (t, invert2) => invert2 ? 1 - t : t, fz = (t, invert2) => invert2 ? 1 - t : t) => { | ||
| const points = []; | ||
| for (let i = 0; i < numPoints; i++) { | ||
| const [x, y, z] = vectorOnLine( | ||
| i / (numPoints - 1), | ||
| p1, | ||
| p2, | ||
| invert, | ||
| fx, | ||
| fy, | ||
| fz | ||
| ); | ||
| points.push([x, y, z]); | ||
| } | ||
| return points; | ||
| }; | ||
| var linearPosition = (t) => { | ||
| return t; | ||
| }; | ||
| var exponentialPosition = (t, reverse = false) => { | ||
| if (reverse) { | ||
| return 1 - (1 - t) ** 2; | ||
| } | ||
| return t ** 2; | ||
| }; | ||
| var quadraticPosition = (t, reverse = false) => { | ||
| if (reverse) { | ||
| return 1 - (1 - t) ** 3; | ||
| } | ||
| return t ** 3; | ||
| }; | ||
| var cubicPosition = (t, reverse = false) => { | ||
| if (reverse) { | ||
| return 1 - (1 - t) ** 4; | ||
| } | ||
| return t ** 4; | ||
| }; | ||
| var quarticPosition = (t, reverse = false) => { | ||
| if (reverse) { | ||
| return 1 - (1 - t) ** 5; | ||
| } | ||
| return t ** 5; | ||
| }; | ||
| var sinusoidalPosition = (t, reverse = false) => { | ||
| if (reverse) { | ||
| return 1 - Math.sin((1 - t) * Math.PI / 2); | ||
| } | ||
| return Math.sin(t * Math.PI / 2); | ||
| }; | ||
| var asinusoidalPosition = (t, reverse = false) => { | ||
| if (reverse) { | ||
| return 1 - Math.asin(1 - t) / (Math.PI / 2); | ||
| } | ||
| return Math.asin(t) / (Math.PI / 2); | ||
| }; | ||
| var arcPosition = (t, reverse = false) => { | ||
| if (reverse) { | ||
| return 1 - Math.sqrt(1 - t ** 2); | ||
| } | ||
| return 1 - Math.sqrt(1 - t); | ||
| }; | ||
| var smoothStepPosition = (t) => { | ||
| return t ** 2 * (3 - 2 * t); | ||
| }; | ||
| var positionFunctions = { | ||
| linearPosition, | ||
| exponentialPosition, | ||
| quadraticPosition, | ||
| cubicPosition, | ||
| quarticPosition, | ||
| sinusoidalPosition, | ||
| asinusoidalPosition, | ||
| arcPosition, | ||
| smoothStepPosition | ||
| }; | ||
| var distance = (p1, p2, hueMode = false) => { | ||
| const a1 = p1[0]; | ||
| const a2 = p2[0]; | ||
| let diffA = 0; | ||
| if (hueMode && a1 !== null && a2 !== null) { | ||
| diffA = Math.min(Math.abs(a1 - a2), 360 - Math.abs(a1 - a2)); | ||
| diffA = diffA / 360; | ||
| } else { | ||
| diffA = a1 === null || a2 === null ? 0 : a1 - a2; | ||
| } | ||
| const a = diffA; | ||
| const b = p1[1] === null || p2[1] === null ? 0 : p2[1] - p1[1]; | ||
| const c = p1[2] === null || p2[2] === null ? 0 : p2[2] - p1[2]; | ||
| return Math.sqrt(a * a + b * b + c * c); | ||
| }; | ||
| var ColorPoint = class { | ||
| constructor({ | ||
| xyz, | ||
| color, | ||
| invertedLightness = false | ||
| } = {}) { | ||
| this.x = 0; | ||
| this.y = 0; | ||
| this.z = 0; | ||
| this.color = [0, 0, 0]; | ||
| this._invertedLightness = false; | ||
| this._invertedLightness = invertedLightness; | ||
| this.positionOrColor({ xyz, color, invertedLightness }); | ||
| } | ||
| positionOrColor({ | ||
| xyz, | ||
| color, | ||
| invertedLightness = false | ||
| }) { | ||
| if (xyz && color || !xyz && !color) { | ||
| throw new Error("Point must be initialized with either x,y,z or hsl"); | ||
| } else if (xyz) { | ||
| this.x = xyz[0]; | ||
| this.y = xyz[1]; | ||
| this.z = xyz[2]; | ||
| this.color = pointToHSL([this.x, this.y, this.z], invertedLightness); | ||
| } else if (color) { | ||
| this.color = color; | ||
| [this.x, this.y, this.z] = hslToPoint(color, invertedLightness); | ||
| } | ||
| } | ||
| set position([x, y, z]) { | ||
| this.x = x; | ||
| this.y = y; | ||
| this.z = z; | ||
| this.color = pointToHSL( | ||
| [this.x, this.y, this.z], | ||
| this._invertedLightness | ||
| ); | ||
| } | ||
| get position() { | ||
| return [this.x, this.y, this.z]; | ||
| } | ||
| set hsl([h, s, l]) { | ||
| this.color = [h, s, l]; | ||
| [this.x, this.y, this.z] = hslToPoint( | ||
| this.color, | ||
| this._invertedLightness | ||
| ); | ||
| } | ||
| get hsl() { | ||
| return this.color; | ||
| } | ||
| get hslCSS() { | ||
| const [h, s, l] = this.color; | ||
| return `hsl(${h.toFixed(2)}, ${(s * 100).toFixed(2)}%, ${(l * 100).toFixed( | ||
| 2 | ||
| )}%)`; | ||
| } | ||
| get oklchCSS() { | ||
| const [h, s, l] = this.color; | ||
| return `oklch(${(l * 100).toFixed(2)}% ${(s * 0.4).toFixed(3)} ${h.toFixed( | ||
| 2 | ||
| )})`; | ||
| } | ||
| get lchCSS() { | ||
| const [h, s, l] = this.color; | ||
| return `lch(${(l * 100).toFixed(2)}% ${(s * 150).toFixed(2)} ${h.toFixed( | ||
| 2 | ||
| )})`; | ||
| } | ||
| shiftHue(angle) { | ||
| this.color[0] = (360 + (this.color[0] + angle)) % 360; | ||
| [this.x, this.y, this.z] = hslToPoint( | ||
| this.color, | ||
| this._invertedLightness | ||
| ); | ||
| } | ||
| }; | ||
| var Poline = class { | ||
| constructor({ | ||
| anchorColors = randomHSLPair(), | ||
| numPoints = 4, | ||
| positionFunction = sinusoidalPosition, | ||
| positionFunctionX, | ||
| positionFunctionY, | ||
| positionFunctionZ, | ||
| closedLoop, | ||
| invertedLightness | ||
| } = { | ||
| anchorColors: randomHSLPair(), | ||
| numPoints: 4, | ||
| positionFunction: sinusoidalPosition, | ||
| closedLoop: false | ||
| }) { | ||
| this._needsUpdate = true; | ||
| this._positionFunctionX = sinusoidalPosition; | ||
| this._positionFunctionY = sinusoidalPosition; | ||
| this._positionFunctionZ = sinusoidalPosition; | ||
| this.connectLastAndFirstAnchor = false; | ||
| this._animationFrame = null; | ||
| this._invertedLightness = false; | ||
| if (!anchorColors || anchorColors.length < 2) { | ||
| throw new Error("Must have at least two anchor colors"); | ||
| } | ||
| this._anchorPoints = anchorColors.map( | ||
| (point) => new ColorPoint({ color: point, invertedLightness }) | ||
| ); | ||
| this._numPoints = numPoints + 2; | ||
| this._positionFunctionX = positionFunctionX || positionFunction || sinusoidalPosition; | ||
| this._positionFunctionY = positionFunctionY || positionFunction || sinusoidalPosition; | ||
| this._positionFunctionZ = positionFunctionZ || positionFunction || sinusoidalPosition; | ||
| this.connectLastAndFirstAnchor = closedLoop || false; | ||
| this._invertedLightness = invertedLightness || false; | ||
| this.updateAnchorPairs(); | ||
| } | ||
| get numPoints() { | ||
| return this._numPoints - 2; | ||
| } | ||
| set numPoints(numPoints) { | ||
| if (numPoints < 1) { | ||
| throw new Error("Must have at least one point"); | ||
| } | ||
| this._numPoints = numPoints + 2; | ||
| this.updateAnchorPairs(); | ||
| } | ||
| set positionFunction(positionFunction) { | ||
| if (Array.isArray(positionFunction)) { | ||
| if (positionFunction.length !== 3) { | ||
| throw new Error("Position function array must have 3 elements"); | ||
| } | ||
| if (typeof positionFunction[0] !== "function" || typeof positionFunction[1] !== "function" || typeof positionFunction[2] !== "function") { | ||
| throw new Error("Position function array must have 3 functions"); | ||
| } | ||
| this._positionFunctionX = positionFunction[0]; | ||
| this._positionFunctionY = positionFunction[1]; | ||
| this._positionFunctionZ = positionFunction[2]; | ||
| } else { | ||
| this._positionFunctionX = positionFunction; | ||
| this._positionFunctionY = positionFunction; | ||
| this._positionFunctionZ = positionFunction; | ||
| } | ||
| this.updateAnchorPairs(); | ||
| } | ||
| get positionFunction() { | ||
| if (this._positionFunctionX === this._positionFunctionY && this._positionFunctionX === this._positionFunctionZ) { | ||
| return this._positionFunctionX; | ||
| } | ||
| return [ | ||
| this._positionFunctionX, | ||
| this._positionFunctionY, | ||
| this._positionFunctionZ | ||
| ]; | ||
| } | ||
| set positionFunctionX(positionFunctionX) { | ||
| this._positionFunctionX = positionFunctionX; | ||
| this.updateAnchorPairs(); | ||
| } | ||
| get positionFunctionX() { | ||
| return this._positionFunctionX; | ||
| } | ||
| set positionFunctionY(positionFunctionY) { | ||
| this._positionFunctionY = positionFunctionY; | ||
| this.updateAnchorPairs(); | ||
| } | ||
| get positionFunctionY() { | ||
| return this._positionFunctionY; | ||
| } | ||
| set positionFunctionZ(positionFunctionZ) { | ||
| this._positionFunctionZ = positionFunctionZ; | ||
| this.updateAnchorPairs(); | ||
| } | ||
| get positionFunctionZ() { | ||
| return this._positionFunctionZ; | ||
| } | ||
| get anchorPoints() { | ||
| return this._anchorPoints; | ||
| } | ||
| set anchorPoints(anchorPoints) { | ||
| this._anchorPoints = anchorPoints; | ||
| this.updateAnchorPairs(); | ||
| } | ||
| updateAnchorPairs() { | ||
| this._anchorPairs = []; | ||
| const anchorPointsLength = this.connectLastAndFirstAnchor ? this.anchorPoints.length : this.anchorPoints.length - 1; | ||
| for (let i = 0; i < anchorPointsLength; i++) { | ||
| const pair = [ | ||
| this.anchorPoints[i], | ||
| this.anchorPoints[(i + 1) % this.anchorPoints.length] | ||
| ]; | ||
| this._anchorPairs.push(pair); | ||
| } | ||
| this.points = this._anchorPairs.map((pair, i) => { | ||
| const p1position = pair[0] ? pair[0].position : [0, 0, 0]; | ||
| const p2position = pair[1] ? pair[1].position : [0, 0, 0]; | ||
| return vectorsOnLine( | ||
| p1position, | ||
| p2position, | ||
| this._numPoints, | ||
| i % 2 ? true : false, | ||
| this.positionFunctionX, | ||
| this.positionFunctionY, | ||
| this.positionFunctionZ | ||
| ).map( | ||
| (p) => new ColorPoint({ xyz: p, invertedLightness: this._invertedLightness }) | ||
| ); | ||
| }); | ||
| } | ||
| addAnchorPoint({ | ||
| xyz, | ||
| color, | ||
| insertAtIndex | ||
| }) { | ||
| const newAnchor = new ColorPoint({ | ||
| xyz, | ||
| color, | ||
| invertedLightness: this._invertedLightness | ||
| }); | ||
| if (insertAtIndex !== void 0) { | ||
| this.anchorPoints.splice(insertAtIndex, 0, newAnchor); | ||
| } else { | ||
| this.anchorPoints.push(newAnchor); | ||
| } | ||
| this.updateAnchorPairs(); | ||
| return newAnchor; | ||
| } | ||
| removeAnchorPoint({ | ||
| point, | ||
| index | ||
| }) { | ||
| if (!point && index === 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 apid; | ||
| if (index !== void 0) { | ||
| apid = index; | ||
| } else if (point) { | ||
| apid = this.anchorPoints.indexOf(point); | ||
| } | ||
| if (apid > -1 && apid < this.anchorPoints.length) { | ||
| this.anchorPoints.splice(apid, 1); | ||
| this.updateAnchorPairs(); | ||
| } else { | ||
| throw new Error("Point not found"); | ||
| } | ||
| } | ||
| updateAnchorPoint({ | ||
| point, | ||
| pointIndex, | ||
| xyz, | ||
| color | ||
| }) { | ||
| if (pointIndex !== void 0) { | ||
| point = this.anchorPoints[pointIndex]; | ||
| } | ||
| if (!point) { | ||
| throw new Error("Must provide a point or pointIndex"); | ||
| } | ||
| if (!xyz && !color) { | ||
| throw new Error("Must provide a new xyz position or color"); | ||
| } | ||
| if (xyz) | ||
| point.position = xyz; | ||
| if (color) | ||
| point.hsl = color; | ||
| this.updateAnchorPairs(); | ||
| return point; | ||
| } | ||
| getClosestAnchorPoint({ | ||
| xyz, | ||
| hsl, | ||
| maxDistance = 1 | ||
| }) { | ||
| if (!xyz && !hsl) { | ||
| throw new Error("Must provide a xyz or hsl"); | ||
| } | ||
| let distances; | ||
| if (xyz) { | ||
| distances = this.anchorPoints.map( | ||
| (anchor) => distance(anchor.position, xyz) | ||
| ); | ||
| } else if (hsl) { | ||
| distances = this.anchorPoints.map( | ||
| (anchor) => distance(anchor.hsl, hsl, true) | ||
| ); | ||
| } | ||
| const minDistance = Math.min(...distances); | ||
| if (minDistance > maxDistance) { | ||
| return null; | ||
| } | ||
| const closestAnchorIndex = distances.indexOf(minDistance); | ||
| return this.anchorPoints[closestAnchorIndex] || null; | ||
| } | ||
| set closedLoop(newStatus) { | ||
| this.connectLastAndFirstAnchor = newStatus; | ||
| this.updateAnchorPairs(); | ||
| } | ||
| get closedLoop() { | ||
| return this.connectLastAndFirstAnchor; | ||
| } | ||
| set invertedLightness(newStatus) { | ||
| this._invertedLightness = newStatus; | ||
| this.updateAnchorPairs(); | ||
| } | ||
| get invertedLightness() { | ||
| return this._invertedLightness; | ||
| } | ||
| /** | ||
| * Returns a flattened array of all points across all segments, | ||
| * removing duplicated anchor points at segment boundaries. | ||
| * | ||
| * Since anchor points exist at both the end of one segment and | ||
| * the beginning of the next, this method keeps only one instance of each. | ||
| * The filter logic keeps the first point (index 0) and then filters out | ||
| * points whose indices are multiples of the segment size (_numPoints), | ||
| * which are the anchor points at the start of each segment (except the first). | ||
| * | ||
| * This approach ensures we get all unique points in the correct order | ||
| * while avoiding duplicated anchor points. | ||
| * | ||
| * @returns {ColorPoint[]} A flat array of unique ColorPoint instances | ||
| */ | ||
| get flattenedPoints() { | ||
| return this.points.flat().filter((p, i) => i != 0 ? i % this._numPoints : true); | ||
| } | ||
| get colors() { | ||
| const colors = this.flattenedPoints.map((p) => p.color); | ||
| if (this.connectLastAndFirstAnchor) { | ||
| colors.pop(); | ||
| } | ||
| return colors; | ||
| } | ||
| cssColors(mode = "hsl") { | ||
| const methods = { | ||
| hsl: (p) => p.hslCSS, | ||
| oklch: (p) => p.oklchCSS, | ||
| lch: (p) => p.lchCSS | ||
| }; | ||
| const cssColors = this.flattenedPoints.map(methods[mode]); | ||
| if (this.connectLastAndFirstAnchor) { | ||
| cssColors.pop(); | ||
| } | ||
| return cssColors; | ||
| } | ||
| get colorsCSS() { | ||
| return this.cssColors("hsl"); | ||
| } | ||
| get colorsCSSlch() { | ||
| return this.cssColors("lch"); | ||
| } | ||
| get colorsCSSoklch() { | ||
| return this.cssColors("oklch"); | ||
| } | ||
| shiftHue(hShift = 20) { | ||
| this.anchorPoints.forEach((p) => p.shiftHue(hShift)); | ||
| this.updateAnchorPairs(); | ||
| } | ||
| }; | ||
| var { p5 } = globalThis; | ||
| if (p5) { | ||
| console.info("p5 detected, adding poline to p5 prototype"); | ||
| const poline = new Poline(); | ||
| p5.prototype.poline = poline; | ||
| const polineColors = () => poline.colors.map( | ||
| (c) => `hsl(${Math.round(c[0])},${c[1] * 100}%,${c[2] * 100}%)` | ||
| ); | ||
| p5.prototype.polineColors = polineColors; | ||
| p5.prototype.registerMethod("polineColors", p5.prototype.polineColors); | ||
| globalThis.poline = poline; | ||
| globalThis.polineColors = polineColors; | ||
| } | ||
| // src/webcomponent.ts | ||
| var namespaceURI = "http://www.w3.org/2000/svg"; | ||
| var svgscale = 100; | ||
| var PolinePicker = class extends HTMLElement { | ||
| constructor() { | ||
| super(); | ||
| this.currentPoint = null; | ||
| this.allowAddPoints = false; | ||
| // Store bound event handlers for cleanup | ||
| this.boundPointerDown = this.handlePointerDown.bind(this); | ||
| this.boundPointerMove = this.handlePointerMove.bind(this); | ||
| this.boundPointerUp = this.handlePointerUp.bind(this); | ||
| this.attachShadow({ mode: "open" }); | ||
| this.interactive = this.hasAttribute("interactive"); | ||
| this.allowAddPoints = this.hasAttribute("allow-add-points"); | ||
| } | ||
| connectedCallback() { | ||
| this.render(); | ||
| if (this.interactive) { | ||
| this.addEventListeners(); | ||
| } | ||
| } | ||
| disconnectedCallback() { | ||
| this.removeEventListeners(); | ||
| } | ||
| setPoline(poline) { | ||
| this.poline = poline; | ||
| this.updateSVG(); | ||
| this.updateLightnessBackground(); | ||
| } | ||
| setAllowAddPoints(allow) { | ||
| this.allowAddPoints = allow; | ||
| } | ||
| addPointAtPosition(x, y) { | ||
| if (!this.poline) | ||
| return null; | ||
| const normalizedX = x / this.svg.clientWidth; | ||
| const normalizedY = y / this.svg.clientHeight; | ||
| const newPoint = this.poline.addAnchorPoint({ | ||
| xyz: [normalizedX, normalizedY, normalizedY] | ||
| }); | ||
| this.updateSVG(); | ||
| this.dispatchPolineChange(); | ||
| return newPoint; | ||
| } | ||
| updateLightnessBackground() { | ||
| const picker = this.shadowRoot?.querySelector(".picker"); | ||
| if (picker && this.poline) { | ||
| if (this.poline.invertedLightness) { | ||
| picker.style.setProperty("--maxL", "#000"); | ||
| picker.style.setProperty("--minL", "#fff"); | ||
| } else { | ||
| picker.style.setProperty("--maxL", "#fff"); | ||
| picker.style.setProperty("--minL", "#000"); | ||
| } | ||
| } | ||
| } | ||
| render() { | ||
| if (!this.shadowRoot) { | ||
| return; | ||
| } | ||
| this.shadowRoot.innerHTML = ` | ||
| <style> | ||
| :host { | ||
| display: block; | ||
| width: 100%; | ||
| } | ||
| .picker { | ||
| position: relative; | ||
| width: 100%; | ||
| aspect-ratio: 1; | ||
| --wheelS: var(--poline-picker-wheel-saturation, .4); | ||
| --wheelL: var(--poline-picker-wheel-lightness, .5); | ||
| --minL: #000; | ||
| --maxL: #fff; | ||
| --grad: hsl(0deg calc(var(--wheelS) * 100%) calc(var(--wheelL) * 100%)) 0deg, hsl(60deg calc(var(--wheelS) * 100%) calc(var(--wheelL) * 100%)) 60deg, hsl(120deg calc(var(--wheelS) * 100%) calc(var(--wheelL) * 100%)) 120deg, hsl(180deg calc(var(--wheelS) * 100%) calc(var(--wheelL) * 100%)) 180deg, hsl(240deg calc(var(--wheelS) * 100%) calc(var(--wheelL) * 100%)) 240deg, hsl(300deg calc(var(--wheelS) * 100%) calc(var(--wheelL) * 100%)) 300deg, hsl(360deg calc(var(--wheelS) * 100%) calc(var(--wheelL) * 100%)) 360deg; | ||
| } | ||
| .picker::before { | ||
| content: ''; | ||
| position: absolute; | ||
| inset: 0; | ||
| border-radius: 50%; | ||
| background: radial-gradient(closest-side, var(--minL), rgba(255, 255, 255, 0), var(--maxL)), | ||
| conic-gradient(from 90deg, var(--grad)); | ||
| z-index: 1; | ||
| } | ||
| svg { | ||
| position: relative; | ||
| z-index: 2; | ||
| overflow: visible; | ||
| width: 100%; | ||
| } | ||
| .wheel__line { | ||
| stroke: var(--poline-picker-line-color, #000); | ||
| stroke-width: calc(0.75 * var(--poline-picker-line-width, 0.2)); | ||
| fill: none; | ||
| } | ||
| .wheel__anchor { | ||
| cursor: grab; | ||
| stroke: var(--poline-picker-line-color, #000); | ||
| stroke-width: var(--poline-picker-line-width, 0.2); | ||
| fill: var(--poline-picker-bg-color, #fff); | ||
| } | ||
| .wheel__anchor:hover { | ||
| cursor: grabbing; | ||
| } | ||
| .wheel__point { | ||
| stroke: var(--poline-picker-line-color, #000); | ||
| stroke-width: calc(0.75 * var(--poline-picker-line-width, 0.2)); | ||
| pointer-events: none; | ||
| } | ||
| </style> | ||
| `; | ||
| this.svg = this.createSVG(); | ||
| const pickerDiv = document.createElement("div"); | ||
| pickerDiv.className = "picker"; | ||
| pickerDiv.appendChild(this.svg); | ||
| this.shadowRoot.appendChild(pickerDiv); | ||
| this.wheel = this.svg.querySelector(".wheel"); | ||
| this.line = this.svg.querySelector(".wheel__line"); | ||
| this.anchors = this.svg.querySelector(".wheel__anchors"); | ||
| this.points = this.svg.querySelector(".wheel__points"); | ||
| if (this.poline) { | ||
| this.updateSVG(); | ||
| } | ||
| } | ||
| createSVG() { | ||
| const svg = document.createElementNS(namespaceURI, "svg"); | ||
| svg.setAttribute("viewBox", `0 0 ${svgscale} ${svgscale}`); | ||
| svg.innerHTML = ` | ||
| <defs> | ||
| <filter id="goo"> | ||
| <feGaussianBlur in="SourceGraphic" stdDeviation="1" result="blur" /> | ||
| <feColorMatrix in="blur" mode="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 18 -7" result="goo" /> | ||
| <feBlend in="SourceGraphic" in2="goo" /> | ||
| </filter> | ||
| </defs> | ||
| <g class="wheel" filter="url(#goo)"> | ||
| <polyline class="wheel__line" points="" /> | ||
| <g class="wheel__anchors"></g> | ||
| <g class="wheel__points"></g> | ||
| </g> | ||
| `; | ||
| return svg; | ||
| } | ||
| updateSVG() { | ||
| if (!this.poline || !this.svg) { | ||
| return; | ||
| } | ||
| const pathPoints = this.poline.flattenedPoints.map((p) => { | ||
| const cartesian = this.pointToCartesian(p); | ||
| if (!cartesian) | ||
| return ""; | ||
| const [x, y] = cartesian; | ||
| return `${x},${y}`; | ||
| }).filter((point) => point !== "").join(" "); | ||
| this.line.setAttribute("points", pathPoints); | ||
| this.anchors.innerHTML = ""; | ||
| this.points.innerHTML = ""; | ||
| this.poline.anchorPoints.forEach((point) => { | ||
| const anchor = this.createCircleElement(point, "wheel__anchor", "2"); | ||
| if (anchor) { | ||
| this.anchors.appendChild(anchor); | ||
| } | ||
| }); | ||
| this.poline.flattenedPoints.forEach((point) => { | ||
| const radius = 0.5 + point.color[1]; | ||
| const circle = this.createCircleElement(point, "wheel__point", radius); | ||
| if (circle) { | ||
| this.points.appendChild(circle); | ||
| } | ||
| }); | ||
| } | ||
| pointToCartesian(point) { | ||
| const half = svgscale / 2; | ||
| const x = half + (point.x - 0.5) * svgscale; | ||
| const y = half + (point.y - 0.5) * svgscale; | ||
| return [x, y]; | ||
| } | ||
| addEventListeners() { | ||
| if (!this.svg) | ||
| return; | ||
| this.svg.addEventListener("pointerdown", this.boundPointerDown); | ||
| this.svg.addEventListener("pointermove", this.boundPointerMove); | ||
| this.svg.addEventListener("pointerup", this.boundPointerUp); | ||
| } | ||
| removeEventListeners() { | ||
| if (!this.svg) | ||
| return; | ||
| this.svg.removeEventListener("pointerdown", this.boundPointerDown); | ||
| this.svg.removeEventListener("pointermove", this.boundPointerMove); | ||
| this.svg.removeEventListener("pointerup", this.boundPointerUp); | ||
| } | ||
| handlePointerDown(e) { | ||
| e.stopPropagation(); | ||
| const { normalizedX, normalizedY } = this.pointerToNormalizedCoordinates(e); | ||
| const closestAnchor = this.poline.getClosestAnchorPoint({ | ||
| xyz: [normalizedX, normalizedY, null], | ||
| maxDistance: 0.1 | ||
| }); | ||
| if (closestAnchor) { | ||
| this.currentPoint = closestAnchor; | ||
| } else if (this.allowAddPoints) { | ||
| this.currentPoint = this.poline.addAnchorPoint({ | ||
| xyz: [normalizedX, normalizedY, normalizedY] | ||
| }); | ||
| this.updateSVG(); | ||
| this.dispatchPolineChange(); | ||
| } | ||
| } | ||
| handlePointerMove(e) { | ||
| if (this.currentPoint) { | ||
| const { normalizedX, normalizedY } = this.pointerToNormalizedCoordinates(e); | ||
| this.poline.updateAnchorPoint({ | ||
| point: this.currentPoint, | ||
| xyz: [normalizedX, normalizedY, this.currentPoint.z] | ||
| }); | ||
| this.updateSVG(); | ||
| this.dispatchPolineChange(); | ||
| } | ||
| } | ||
| handlePointerUp() { | ||
| this.currentPoint = null; | ||
| } | ||
| getPointerPosition(e) { | ||
| const rect = this.svg.getBoundingClientRect(); | ||
| return { | ||
| x: e.clientX - rect.left, | ||
| y: e.clientY - rect.top | ||
| }; | ||
| } | ||
| pointerToNormalizedCoordinates(e) { | ||
| const svgRect = this.svg.getBoundingClientRect(); | ||
| const svgX = (e.clientX - svgRect.left) / svgRect.width * svgscale; | ||
| const svgY = (e.clientY - svgRect.top) / svgRect.height * svgscale; | ||
| return { | ||
| normalizedX: svgX / svgscale, | ||
| normalizedY: svgY / svgscale | ||
| }; | ||
| } | ||
| createCircleElement(point, className, radius) { | ||
| const cartesian = this.pointToCartesian(point); | ||
| if (!cartesian) | ||
| return null; | ||
| const [x = 0, y = 0] = cartesian; | ||
| const circle = document.createElementNS(namespaceURI, "circle"); | ||
| circle.setAttribute("class", className); | ||
| circle.setAttribute("cx", x.toString()); | ||
| circle.setAttribute("cy", y.toString()); | ||
| circle.setAttribute("r", radius.toString()); | ||
| circle.setAttribute("fill", point.hslCSS); | ||
| return circle; | ||
| } | ||
| dispatchPolineChange() { | ||
| this.dispatchEvent( | ||
| new CustomEvent("poline-change", { | ||
| detail: { poline: this.poline } | ||
| }) | ||
| ); | ||
| } | ||
| }; | ||
| customElements.define("poline-picker", PolinePicker); | ||
| export { | ||
| Poline, | ||
| PolinePicker, | ||
| positionFunctions | ||
| }; |
| import { Poline, positionFunctions, ColorPoint } from "./index"; | ||
| export { Poline, positionFunctions }; | ||
| export declare class PolinePicker extends HTMLElement { | ||
| private poline; | ||
| private svg; | ||
| private interactive; | ||
| private line; | ||
| private wheel; | ||
| private anchors; | ||
| private points; | ||
| private currentPoint; | ||
| private allowAddPoints; | ||
| private boundPointerDown; | ||
| private boundPointerMove; | ||
| private boundPointerUp; | ||
| constructor(); | ||
| connectedCallback(): void; | ||
| disconnectedCallback(): void; | ||
| setPoline(poline: Poline): void; | ||
| setAllowAddPoints(allow: boolean): void; | ||
| addPointAtPosition(x: number, y: number): ColorPoint | null; | ||
| private updateLightnessBackground; | ||
| private render; | ||
| private createSVG; | ||
| updateSVG(): void; | ||
| private pointToCartesian; | ||
| private addEventListeners; | ||
| private removeEventListeners; | ||
| private handlePointerDown; | ||
| private handlePointerMove; | ||
| private handlePointerUp; | ||
| private getPointerPosition; | ||
| private pointerToNormalizedCoordinates; | ||
| private createCircleElement; | ||
| private dispatchPolineChange; | ||
| } |
| import { Poline, positionFunctions, ColorPoint } from "./index"; | ||
| // Re-export for convenience when using the picker standalone | ||
| export { Poline, positionFunctions }; | ||
| const namespaceURI = "http://www.w3.org/2000/svg"; | ||
| const svgscale = 100; | ||
| export class PolinePicker extends HTMLElement { | ||
| private poline: Poline; | ||
| private svg: SVGElement; | ||
| private interactive: boolean; | ||
| private line: SVGPolylineElement; | ||
| private wheel: SVGGElement; | ||
| private anchors: SVGGElement; | ||
| private points: SVGGElement; | ||
| private currentPoint: ColorPoint | null = null; | ||
| private allowAddPoints = false; | ||
| // Store bound event handlers for cleanup | ||
| private boundPointerDown = this.handlePointerDown.bind(this); | ||
| private boundPointerMove = this.handlePointerMove.bind(this); | ||
| private boundPointerUp = this.handlePointerUp.bind(this); | ||
| constructor() { | ||
| super(); | ||
| this.attachShadow({ mode: "open" }); | ||
| this.interactive = this.hasAttribute("interactive"); | ||
| this.allowAddPoints = this.hasAttribute("allow-add-points"); | ||
| } | ||
| connectedCallback() { | ||
| this.render(); | ||
| if (this.interactive) { | ||
| this.addEventListeners(); | ||
| } | ||
| } | ||
| disconnectedCallback() { | ||
| // Clean up event listeners when component is removed from DOM | ||
| this.removeEventListeners(); | ||
| } | ||
| setPoline(poline: Poline) { | ||
| this.poline = poline; | ||
| this.updateSVG(); | ||
| this.updateLightnessBackground(); | ||
| } | ||
| setAllowAddPoints(allow: boolean) { | ||
| this.allowAddPoints = allow; | ||
| } | ||
| addPointAtPosition(x: number, y: number) { | ||
| if (!this.poline) return null; | ||
| // Convert to normalized coordinates (0-1) | ||
| const normalizedX = x / this.svg.clientWidth; | ||
| const normalizedY = y / this.svg.clientHeight; | ||
| // Use the normalized Y coordinate as the Z (lightness) coordinate | ||
| const newPoint = this.poline.addAnchorPoint({ | ||
| xyz: [normalizedX, normalizedY, normalizedY], | ||
| }); | ||
| this.updateSVG(); | ||
| this.dispatchPolineChange(); | ||
| return newPoint; | ||
| } | ||
| private updateLightnessBackground() { | ||
| const picker = this.shadowRoot?.querySelector(".picker") as HTMLElement; | ||
| if (picker && this.poline) { | ||
| if (this.poline.invertedLightness) { | ||
| picker.style.setProperty("--maxL", "#000"); | ||
| picker.style.setProperty("--minL", "#fff"); | ||
| } else { | ||
| picker.style.setProperty("--maxL", "#fff"); | ||
| picker.style.setProperty("--minL", "#000"); | ||
| } | ||
| } | ||
| } | ||
| private render() { | ||
| if (!this.shadowRoot) { | ||
| return; | ||
| } | ||
| this.shadowRoot.innerHTML = ` | ||
| <style> | ||
| :host { | ||
| display: block; | ||
| width: 100%; | ||
| } | ||
| .picker { | ||
| position: relative; | ||
| width: 100%; | ||
| aspect-ratio: 1; | ||
| --wheelS: var(--poline-picker-wheel-saturation, .4); | ||
| --wheelL: var(--poline-picker-wheel-lightness, .5); | ||
| --minL: #000; | ||
| --maxL: #fff; | ||
| --grad: hsl(0deg calc(var(--wheelS) * 100%) calc(var(--wheelL) * 100%)) 0deg, hsl(60deg calc(var(--wheelS) * 100%) calc(var(--wheelL) * 100%)) 60deg, hsl(120deg calc(var(--wheelS) * 100%) calc(var(--wheelL) * 100%)) 120deg, hsl(180deg calc(var(--wheelS) * 100%) calc(var(--wheelL) * 100%)) 180deg, hsl(240deg calc(var(--wheelS) * 100%) calc(var(--wheelL) * 100%)) 240deg, hsl(300deg calc(var(--wheelS) * 100%) calc(var(--wheelL) * 100%)) 300deg, hsl(360deg calc(var(--wheelS) * 100%) calc(var(--wheelL) * 100%)) 360deg; | ||
| } | ||
| .picker::before { | ||
| content: ''; | ||
| position: absolute; | ||
| inset: 0; | ||
| border-radius: 50%; | ||
| background: radial-gradient(closest-side, var(--minL), rgba(255, 255, 255, 0), var(--maxL)), | ||
| conic-gradient(from 90deg, var(--grad)); | ||
| z-index: 1; | ||
| } | ||
| svg { | ||
| position: relative; | ||
| z-index: 2; | ||
| overflow: visible; | ||
| width: 100%; | ||
| } | ||
| .wheel__line { | ||
| stroke: var(--poline-picker-line-color, #000); | ||
| stroke-width: calc(0.75 * var(--poline-picker-line-width, 0.2)); | ||
| fill: none; | ||
| } | ||
| .wheel__anchor { | ||
| cursor: grab; | ||
| stroke: var(--poline-picker-line-color, #000); | ||
| stroke-width: var(--poline-picker-line-width, 0.2); | ||
| fill: var(--poline-picker-bg-color, #fff); | ||
| } | ||
| .wheel__anchor:hover { | ||
| cursor: grabbing; | ||
| } | ||
| .wheel__point { | ||
| stroke: var(--poline-picker-line-color, #000); | ||
| stroke-width: calc(0.75 * var(--poline-picker-line-width, 0.2)); | ||
| pointer-events: none; | ||
| } | ||
| </style> | ||
| `; | ||
| this.svg = this.createSVG(); | ||
| const pickerDiv = document.createElement("div"); | ||
| pickerDiv.className = "picker"; | ||
| pickerDiv.appendChild(this.svg); | ||
| this.shadowRoot.appendChild(pickerDiv); | ||
| this.wheel = this.svg.querySelector(".wheel") as SVGGElement; | ||
| this.line = this.svg.querySelector(".wheel__line") as SVGPolylineElement; | ||
| this.anchors = this.svg.querySelector(".wheel__anchors") as SVGGElement; | ||
| this.points = this.svg.querySelector(".wheel__points") as SVGGElement; | ||
| if (this.poline) { | ||
| this.updateSVG(); | ||
| } | ||
| } | ||
| private createSVG() { | ||
| const svg = document.createElementNS(namespaceURI, "svg"); | ||
| svg.setAttribute("viewBox", `0 0 ${svgscale} ${svgscale}`); | ||
| svg.innerHTML = ` | ||
| <defs> | ||
| <filter id="goo"> | ||
| <feGaussianBlur in="SourceGraphic" stdDeviation="1" result="blur" /> | ||
| <feColorMatrix in="blur" mode="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 18 -7" result="goo" /> | ||
| <feBlend in="SourceGraphic" in2="goo" /> | ||
| </filter> | ||
| </defs> | ||
| <g class="wheel" filter="url(#goo)"> | ||
| <polyline class="wheel__line" points="" /> | ||
| <g class="wheel__anchors"></g> | ||
| <g class="wheel__points"></g> | ||
| </g> | ||
| `; | ||
| return svg; | ||
| } | ||
| public updateSVG() { | ||
| if (!this.poline || !this.svg) { | ||
| return; | ||
| } | ||
| // 1) Draw line paths | ||
| const pathPoints = this.poline.flattenedPoints | ||
| .map((p) => { | ||
| const cartesian = this.pointToCartesian(p); | ||
| if (!cartesian) return ""; | ||
| const [x, y] = cartesian; | ||
| return `${x},${y}`; | ||
| }) | ||
| .filter((point) => point !== "") | ||
| .join(" "); | ||
| this.line.setAttribute("points", pathPoints); | ||
| // Clear existing elements | ||
| this.anchors.innerHTML = ""; | ||
| this.points.innerHTML = ""; | ||
| // 2) Draw anchor points (white dots at the ends) | ||
| this.poline.anchorPoints.forEach((point) => { | ||
| const anchor = this.createCircleElement(point, "wheel__anchor", "2"); | ||
| if (anchor) { | ||
| this.anchors.appendChild(anchor); | ||
| } | ||
| }); | ||
| // 3) Draw intermediate points (sample dots along the lines) - TOP layer | ||
| this.poline.flattenedPoints.forEach((point) => { | ||
| const radius = 0.5 + point.color[1]; | ||
| const circle = this.createCircleElement(point, "wheel__point", radius); | ||
| if (circle) { | ||
| this.points.appendChild(circle); | ||
| } | ||
| }); | ||
| } | ||
| private pointToCartesian(point: ColorPoint) { | ||
| const half = svgscale / 2; | ||
| const x = half + (point.x - 0.5) * svgscale; | ||
| const y = half + (point.y - 0.5) * svgscale; | ||
| return [x, y]; | ||
| } | ||
| private addEventListeners() { | ||
| if (!this.svg) return; | ||
| this.svg.addEventListener("pointerdown", this.boundPointerDown); | ||
| this.svg.addEventListener("pointermove", this.boundPointerMove); | ||
| this.svg.addEventListener("pointerup", this.boundPointerUp); | ||
| } | ||
| private removeEventListeners() { | ||
| if (!this.svg) return; | ||
| this.svg.removeEventListener("pointerdown", this.boundPointerDown); | ||
| this.svg.removeEventListener("pointermove", this.boundPointerMove); | ||
| this.svg.removeEventListener("pointerup", this.boundPointerUp); | ||
| } | ||
| private handlePointerDown(e: PointerEvent) { | ||
| e.stopPropagation(); | ||
| const { normalizedX, normalizedY } = this.pointerToNormalizedCoordinates(e); | ||
| const closestAnchor = this.poline.getClosestAnchorPoint({ | ||
| xyz: [normalizedX, normalizedY, null], | ||
| maxDistance: 0.1, | ||
| }); | ||
| if (closestAnchor) { | ||
| this.currentPoint = closestAnchor; | ||
| } else if (this.allowAddPoints) { | ||
| this.currentPoint = this.poline.addAnchorPoint({ | ||
| xyz: [normalizedX, normalizedY, normalizedY], | ||
| }); | ||
| this.updateSVG(); | ||
| this.dispatchPolineChange(); | ||
| } | ||
| } | ||
| private handlePointerMove(e: PointerEvent) { | ||
| if (this.currentPoint) { | ||
| const { normalizedX, normalizedY } = | ||
| this.pointerToNormalizedCoordinates(e); | ||
| this.poline.updateAnchorPoint({ | ||
| point: this.currentPoint, | ||
| xyz: [normalizedX, normalizedY, this.currentPoint.z], | ||
| }); | ||
| this.updateSVG(); | ||
| this.dispatchPolineChange(); | ||
| } | ||
| } | ||
| private handlePointerUp() { | ||
| this.currentPoint = null; | ||
| } | ||
| private getPointerPosition(e: PointerEvent) { | ||
| const rect = this.svg.getBoundingClientRect(); | ||
| return { | ||
| x: e.clientX - rect.left, | ||
| y: e.clientY - rect.top, | ||
| }; | ||
| } | ||
| private pointerToNormalizedCoordinates(e: PointerEvent) { | ||
| const svgRect = this.svg.getBoundingClientRect(); | ||
| const svgX = ((e.clientX - svgRect.left) / svgRect.width) * svgscale; | ||
| const svgY = ((e.clientY - svgRect.top) / svgRect.height) * svgscale; | ||
| return { | ||
| normalizedX: svgX / svgscale, | ||
| normalizedY: svgY / svgscale, | ||
| }; | ||
| } | ||
| private createCircleElement( | ||
| point: ColorPoint, | ||
| className: string, | ||
| radius: number | string | ||
| ): SVGCircleElement | null { | ||
| const cartesian = this.pointToCartesian(point); | ||
| if (!cartesian) return null; | ||
| const [x = 0, y = 0] = cartesian; | ||
| const circle = document.createElementNS(namespaceURI, "circle"); | ||
| circle.setAttribute("class", className); | ||
| circle.setAttribute("cx", x.toString()); | ||
| circle.setAttribute("cy", y.toString()); | ||
| circle.setAttribute("r", radius.toString()); | ||
| circle.setAttribute("fill", point.hslCSS); | ||
| return circle; | ||
| } | ||
| private dispatchPolineChange() { | ||
| this.dispatchEvent( | ||
| new CustomEvent("poline-change", { | ||
| detail: { poline: this.poline }, | ||
| }) | ||
| ); | ||
| } | ||
| } | ||
| customElements.define("poline-picker", PolinePicker); |
+6
| # https://tea.xyz/what-is-this-file | ||
| --- | ||
| version: 1.0.0 | ||
| codeOwners: | ||
| - '0xFA64435d1281921E36b90CeA9a1fbf0e5c408e65' | ||
| quorum: 1 |
@@ -35,4 +35,10 @@ # Simple workflow for deploying static content to GitHub Pages | ||
| uses: actions/configure-pages@v2 | ||
| - name: Use Node.js ${{ matrix.node-version }} | ||
| uses: actions/setup-node@v3 | ||
| with: | ||
| node-version: ${{ matrix.node-version }} | ||
| - run: npm ci | ||
| - run: npm run build --if-present | ||
| - name: Upload artifact | ||
| uses: actions/upload-pages-artifact@v1 | ||
| uses: actions/upload-pages-artifact@v3 | ||
| with: | ||
@@ -43,2 +49,2 @@ # Upload entire repository | ||
| id: deployment | ||
| uses: actions/deploy-pages@v1 | ||
| uses: actions/deploy-pages@v4 |
+63
-0
@@ -92,1 +92,64 @@ import { build } from "esbuild"; | ||
| }); | ||
| // Bundled CJS | ||
| build({ | ||
| entryPoints: ["./src/webcomponent.ts"], | ||
| logLevel: "info", | ||
| bundle: true, | ||
| format: "cjs", | ||
| outfile: "dist/picker.cjs", | ||
| }); | ||
| // Bundled CJS, minified | ||
| build({ | ||
| entryPoints: ["./src/webcomponent.ts"], | ||
| logLevel: "info", | ||
| bundle: true, | ||
| minify: true, | ||
| format: "cjs", | ||
| outfile: "dist/picker.min.cjs", | ||
| }); | ||
| // Bundled ESM | ||
| build({ | ||
| entryPoints: ["./src/webcomponent.ts"], | ||
| logLevel: "info", | ||
| bundle: true, | ||
| format: "esm", | ||
| target: "es2020", | ||
| outfile: "dist/picker.mjs", | ||
| }); | ||
| // Bundled ESM, minified | ||
| build({ | ||
| entryPoints: ["./src/webcomponent.ts"], | ||
| logLevel: "info", | ||
| bundle: true, | ||
| minify: true, | ||
| format: "esm", | ||
| target: "es2020", | ||
| outfile: "dist/picker.min.mjs", | ||
| }); | ||
| // Bundled IIFE | ||
| build({ | ||
| entryPoints: ["./src/webcomponent.ts"], | ||
| logLevel: "info", | ||
| bundle: true, | ||
| format: "iife", | ||
| target: "node14", | ||
| globalName: "polinePicker", | ||
| outfile: "dist/picker.js", | ||
| }); | ||
| // Bundled IIFE, minified | ||
| build({ | ||
| entryPoints: ["./src/webcomponent.ts"], | ||
| logLevel: "info", | ||
| bundle: true, | ||
| minify: true, | ||
| format: "iife", | ||
| target: "es6", | ||
| globalName: "polinePicker", | ||
| outfile: "dist/picker.min.js", | ||
| }); |
+36
-12
@@ -24,2 +24,3 @@ "use strict"; | ||
| __export(src_exports, { | ||
| ColorPoint: () => ColorPoint, | ||
| Poline: () => Poline, | ||
@@ -135,3 +136,3 @@ hslToPoint: () => hslToPoint, | ||
| if (reverse) { | ||
| return Math.sqrt(1 - __pow(1 - t, 2)); | ||
| return 1 - Math.sqrt(1 - __pow(t, 2)); | ||
| } | ||
@@ -170,3 +171,7 @@ return 1 - Math.sqrt(1 - t); | ||
| var ColorPoint = class { | ||
| constructor({ xyz, color, invertedLightness } = {}) { | ||
| constructor({ | ||
| xyz, | ||
| color, | ||
| invertedLightness = false | ||
| } = {}) { | ||
| this.x = 0; | ||
@@ -177,7 +182,11 @@ this.y = 0; | ||
| this._invertedLightness = false; | ||
| this._invertedLightness = invertedLightness || false; | ||
| this._invertedLightness = invertedLightness; | ||
| this.positionOrColor({ xyz, color, invertedLightness }); | ||
| } | ||
| positionOrColor({ xyz, color, invertedLightness }) { | ||
| if (xyz && color) { | ||
| positionOrColor({ | ||
| xyz, | ||
| color, | ||
| invertedLightness = false | ||
| }) { | ||
| if (xyz && color || !xyz && !color) { | ||
| throw new Error("Point must be initialized with either x,y,z or hsl"); | ||
@@ -188,9 +197,6 @@ } else if (xyz) { | ||
| this.z = xyz[2]; | ||
| this.color = pointToHSL( | ||
| [this.x, this.y, this.z], | ||
| invertedLightness || false | ||
| ); | ||
| this.color = pointToHSL([this.x, this.y, this.z], invertedLightness); | ||
| } else if (color) { | ||
| this.color = color; | ||
| [this.x, this.y, this.z] = hslToPoint(color, invertedLightness || false); | ||
| [this.x, this.y, this.z] = hslToPoint(color, invertedLightness); | ||
| } | ||
@@ -385,3 +391,3 @@ } | ||
| }); | ||
| if (insertAtIndex) { | ||
| if (insertAtIndex !== void 0) { | ||
| this.anchorPoints.splice(insertAtIndex, 0, newAnchor); | ||
@@ -401,2 +407,5 @@ } else { | ||
| } | ||
| if (this.anchorPoints.length < 3) { | ||
| throw new Error("Must have at least two anchor points"); | ||
| } | ||
| let apid; | ||
@@ -421,3 +430,3 @@ if (index !== void 0) { | ||
| }) { | ||
| if (pointIndex) { | ||
| if (pointIndex !== void 0) { | ||
| point = this.anchorPoints[pointIndex]; | ||
@@ -477,2 +486,17 @@ } | ||
| } | ||
| /** | ||
| * Returns a flattened array of all points across all segments, | ||
| * removing duplicated anchor points at segment boundaries. | ||
| * | ||
| * Since anchor points exist at both the end of one segment and | ||
| * the beginning of the next, this method keeps only one instance of each. | ||
| * The filter logic keeps the first point (index 0) and then filters out | ||
| * points whose indices are multiples of the segment size (_numPoints), | ||
| * which are the anchor points at the start of each segment (except the first). | ||
| * | ||
| * This approach ensures we get all unique points in the correct order | ||
| * while avoiding duplicated anchor points. | ||
| * | ||
| * @returns {ColorPoint[]} A flat array of unique ColorPoint instances | ||
| */ | ||
| get flattenedPoints() { | ||
@@ -479,0 +503,0 @@ return this.points.flat().filter((p, i) => i != 0 ? i % this._numPoints : true); |
+25
-12
@@ -13,5 +13,4 @@ export type FuncNumberReturn = (arg0: number) => Vector2; | ||
| * @example | ||
| * pointToHSL(0.5, 0.5, 1) // [0, 1, 0.5] | ||
| * pointToHSL(0.5, 0.5, 0) // [0, 1, 0] | ||
| * pointToHSL(0.5, 0.5, 1) // [0, 1, 1] | ||
| * pointToHSL([0.5, 0.5, 1]) // [0, 1, 0.5] | ||
| * pointToHSL([0.5, 0.5, 0]) // [0, 1, 0] | ||
| **/ | ||
@@ -52,3 +51,3 @@ export declare const pointToHSL: (xyz: [number, number, number], invertedLightness: boolean) => [number, number, number]; | ||
| }; | ||
| declare class ColorPoint { | ||
| export declare class ColorPoint { | ||
| x: number; | ||
@@ -59,4 +58,4 @@ y: number; | ||
| private _invertedLightness; | ||
| constructor({ xyz, color, invertedLightness }?: ColorPointCollection); | ||
| positionOrColor({ xyz, color, invertedLightness }: ColorPointCollection): void; | ||
| constructor({ xyz, color, invertedLightness, }?: ColorPointCollection); | ||
| positionOrColor({ xyz, color, invertedLightness, }: ColorPointCollection): void; | ||
| set position([x, y, z]: Vector3); | ||
@@ -108,3 +107,3 @@ get position(): Vector3; | ||
| addAnchorPoint({ xyz, color, insertAtIndex, }: ColorPointCollection & { | ||
| insertAtIndex: number; | ||
| insertAtIndex?: number; | ||
| }): ColorPoint; | ||
@@ -128,10 +127,24 @@ removeAnchorPoint({ point, index, }: { | ||
| get invertedLightness(): boolean; | ||
| /** | ||
| * Returns a flattened array of all points across all segments, | ||
| * removing duplicated anchor points at segment boundaries. | ||
| * | ||
| * Since anchor points exist at both the end of one segment and | ||
| * the beginning of the next, this method keeps only one instance of each. | ||
| * The filter logic keeps the first point (index 0) and then filters out | ||
| * points whose indices are multiples of the segment size (_numPoints), | ||
| * which are the anchor points at the start of each segment (except the first). | ||
| * | ||
| * This approach ensures we get all unique points in the correct order | ||
| * while avoiding duplicated anchor points. | ||
| * | ||
| * @returns {ColorPoint[]} A flat array of unique ColorPoint instances | ||
| */ | ||
| get flattenedPoints(): ColorPoint[]; | ||
| get colors(): [number, number, number][]; | ||
| cssColors(mode?: "hsl" | "oklch" | "lch"): any[]; | ||
| get colorsCSS(): any[]; | ||
| get colorsCSSlch(): any[]; | ||
| get colorsCSSoklch(): any[]; | ||
| cssColors(mode?: "hsl" | "oklch" | "lch"): string[]; | ||
| get colorsCSS(): string[]; | ||
| get colorsCSSlch(): string[]; | ||
| get colorsCSSoklch(): string[]; | ||
| shiftHue(hShift?: number): void; | ||
| } | ||
| export {}; |
+36
-12
@@ -24,2 +24,3 @@ "use strict"; | ||
| __export(src_exports, { | ||
| ColorPoint: () => ColorPoint, | ||
| Poline: () => Poline, | ||
@@ -134,3 +135,3 @@ hslToPoint: () => hslToPoint, | ||
| if (reverse) { | ||
| return Math.sqrt(1 - (1 - t) ** 2); | ||
| return 1 - Math.sqrt(1 - t ** 2); | ||
| } | ||
@@ -169,3 +170,7 @@ return 1 - Math.sqrt(1 - t); | ||
| var ColorPoint = class { | ||
| constructor({ xyz, color, invertedLightness } = {}) { | ||
| constructor({ | ||
| xyz, | ||
| color, | ||
| invertedLightness = false | ||
| } = {}) { | ||
| this.x = 0; | ||
@@ -176,7 +181,11 @@ this.y = 0; | ||
| this._invertedLightness = false; | ||
| this._invertedLightness = invertedLightness || false; | ||
| this._invertedLightness = invertedLightness; | ||
| this.positionOrColor({ xyz, color, invertedLightness }); | ||
| } | ||
| positionOrColor({ xyz, color, invertedLightness }) { | ||
| if (xyz && color) { | ||
| positionOrColor({ | ||
| xyz, | ||
| color, | ||
| invertedLightness = false | ||
| }) { | ||
| if (xyz && color || !xyz && !color) { | ||
| throw new Error("Point must be initialized with either x,y,z or hsl"); | ||
@@ -187,9 +196,6 @@ } else if (xyz) { | ||
| this.z = xyz[2]; | ||
| this.color = pointToHSL( | ||
| [this.x, this.y, this.z], | ||
| invertedLightness || false | ||
| ); | ||
| this.color = pointToHSL([this.x, this.y, this.z], invertedLightness); | ||
| } else if (color) { | ||
| this.color = color; | ||
| [this.x, this.y, this.z] = hslToPoint(color, invertedLightness || false); | ||
| [this.x, this.y, this.z] = hslToPoint(color, invertedLightness); | ||
| } | ||
@@ -384,3 +390,3 @@ } | ||
| }); | ||
| if (insertAtIndex) { | ||
| if (insertAtIndex !== void 0) { | ||
| this.anchorPoints.splice(insertAtIndex, 0, newAnchor); | ||
@@ -400,2 +406,5 @@ } else { | ||
| } | ||
| if (this.anchorPoints.length < 3) { | ||
| throw new Error("Must have at least two anchor points"); | ||
| } | ||
| let apid; | ||
@@ -420,3 +429,3 @@ if (index !== void 0) { | ||
| }) { | ||
| if (pointIndex) { | ||
| if (pointIndex !== void 0) { | ||
| point = this.anchorPoints[pointIndex]; | ||
@@ -476,2 +485,17 @@ } | ||
| } | ||
| /** | ||
| * Returns a flattened array of all points across all segments, | ||
| * removing duplicated anchor points at segment boundaries. | ||
| * | ||
| * Since anchor points exist at both the end of one segment and | ||
| * the beginning of the next, this method keeps only one instance of each. | ||
| * The filter logic keeps the first point (index 0) and then filters out | ||
| * points whose indices are multiples of the segment size (_numPoints), | ||
| * which are the anchor points at the start of each segment (except the first). | ||
| * | ||
| * This approach ensures we get all unique points in the correct order | ||
| * while avoiding duplicated anchor points. | ||
| * | ||
| * @returns {ColorPoint[]} A flat array of unique ColorPoint instances | ||
| */ | ||
| get flattenedPoints() { | ||
@@ -478,0 +502,0 @@ return this.points.flat().filter((p, i) => i != 0 ? i % this._numPoints : true); |
@@ -1,1 +0,1 @@ | ||
| "use strict";var F=Object.defineProperty;var C=Object.getOwnPropertyDescriptor;var V=Object.getOwnPropertyNames;var x=Object.prototype.hasOwnProperty;var a=Math.pow;var A=(n,t)=>{for(var o in t)F(n,o,{get:t[o],enumerable:!0})},y=(n,t,o,i)=>{if(t&&typeof t=="object"||typeof t=="function")for(let s of V(t))!x.call(n,s)&&s!==o&&F(n,s,{get:()=>t[s],enumerable:!(i=C(t,s))||i.enumerable});return n};var L=n=>y(F({},"__esModule",{value:!0}),n);var T={};A(T,{Poline:()=>f,hslToPoint:()=>m,pointToHSL:()=>_,positionFunctions:()=>q,randomHSLPair:()=>M,randomHSLTriple:()=>w});module.exports=L(T);var _=(n,t)=>{let[o,i,s]=n,c=.5,h=.5,e=Math.atan2(i-h,o-c)*(180/Math.PI);e=(360+e)%360;let l=s,p=Math.sqrt(Math.pow(i-h,2)+Math.pow(o-c,2))/c;return[e,l,t?1-p:p]},m=(n,t)=>{let[o,i,s]=n,c=.5,h=.5,r=o/(180/Math.PI),e=(t?1-s:s)*c,l=c+e*Math.cos(r),d=h+e*Math.sin(r);return[l,d,i]},M=(n=Math.random()*360,t=[Math.random(),Math.random()],o=[.75+Math.random()*.2,.3+Math.random()*.2])=>[[n,t[0],o[0]],[(n+60+Math.random()*180)%360,t[1],o[1]]],w=(n=Math.random()*360,t=[Math.random(),Math.random(),Math.random()],o=[.75+Math.random()*.2,Math.random()*.2,.75+Math.random()*.2])=>[[n,t[0],o[0]],[(n+60+Math.random()*180)%360,t[1],o[1]],[(n+60+Math.random()*180)%360,t[2],o[2]]],S=(n,t,o,i=!1,s=(r,e)=>e?1-r:r,c=(r,e)=>e?1-r:r,h=(r,e)=>e?1-r:r)=>{let r=s(n,i),e=c(n,i),l=h(n,i),d=(1-r)*t[0]+r*o[0],p=(1-e)*t[1]+e*o[1],v=(1-l)*t[2]+l*o[2];return[d,p,v]},X=(n,t,o=4,i=!1,s=(r,e)=>e?1-r:r,c=(r,e)=>e?1-r:r,h=(r,e)=>e?1-r:r)=>{let r=[];for(let e=0;e<o;e++){let[l,d,p]=S(e/(o-1),n,t,i,s,c,h);r.push([l,d,p])}return r},z=n=>n,Y=(n,t=!1)=>t?1-a(1-n,2):a(n,2),Z=(n,t=!1)=>t?1-a(1-n,3):a(n,3),$=(n,t=!1)=>t?1-a(1-n,4):a(n,4),E=(n,t=!1)=>t?1-a(1-n,5):a(n,5),u=(n,t=!1)=>t?1-Math.sin((1-n)*Math.PI/2):Math.sin(n*Math.PI/2),I=(n,t=!1)=>t?1-Math.asin(1-n)/(Math.PI/2):Math.asin(n)/(Math.PI/2),O=(n,t=!1)=>t?Math.sqrt(1-a(1-n,2)):1-Math.sqrt(1-n),k=n=>a(n,2)*(3-2*n),q={linearPosition:z,exponentialPosition:Y,quadraticPosition:Z,cubicPosition:$,quarticPosition:E,sinusoidalPosition:u,asinusoidalPosition:I,arcPosition:O,smoothStepPosition:k},g=(n,t,o=!1)=>{let i=n[0],s=t[0],c=0;o&&i!==null&&s!==null?(c=Math.min(Math.abs(i-s),360-Math.abs(i-s)),c=c/360):c=i===null||s===null?0:i-s;let h=c,r=n[1]===null||t[1]===null?0:t[1]-n[1],e=n[2]===null||t[2]===null?0:t[2]-n[2];return Math.sqrt(h*h+r*r+e*e)},b=class{constructor({xyz:t,color:o,invertedLightness:i}={}){this.x=0;this.y=0;this.z=0;this.color=[0,0,0];this._invertedLightness=!1;this._invertedLightness=i||!1,this.positionOrColor({xyz:t,color:o,invertedLightness:i})}positionOrColor({xyz:t,color:o,invertedLightness:i}){if(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=_([this.x,this.y,this.z],i||!1)):o&&(this.color=o,[this.x,this.y,this.z]=m(o,i||!1))}set position([t,o,i]){this.x=t,this.y=o,this.z=i,this.color=_([this.x,this.y,this.z],this._invertedLightness)}get position(){return[this.x,this.y,this.z]}set hsl([t,o,i]){this.color=[t,o,i],[this.x,this.y,this.z]=m(this.color,this._invertedLightness)}get hsl(){return this.color}get hslCSS(){let[t,o,i]=this.color;return`hsl(${t.toFixed(2)}, ${(o*100).toFixed(2)}%, ${(i*100).toFixed(2)}%)`}get oklchCSS(){let[t,o,i]=this.color;return`oklch(${(i*100).toFixed(2)}% ${(o*.4).toFixed(3)} ${t.toFixed(2)})`}get lchCSS(){let[t,o,i]=this.color;return`lch(${(i*100).toFixed(2)}% ${(o*150).toFixed(2)} ${t.toFixed(2)})`}shiftHue(t){this.color[0]=(360+(this.color[0]+t))%360,[this.x,this.y,this.z]=m(this.color,this._invertedLightness)}},f=class{constructor({anchorColors:t=M(),numPoints:o=4,positionFunction:i=u,positionFunctionX:s,positionFunctionY:c,positionFunctionZ:h,closedLoop:r,invertedLightness:e}={anchorColors:M(),numPoints:4,positionFunction:u,closedLoop:!1}){this._needsUpdate=!0;this._positionFunctionX=u;this._positionFunctionY=u;this._positionFunctionZ=u;this.connectLastAndFirstAnchor=!1;this._animationFrame=null;this._invertedLightness=!1;if(!t||t.length<2)throw new Error("Must have at least two anchor colors");this._anchorPoints=t.map(l=>new b({color:l,invertedLightness:e})),this._numPoints=o+2,this._positionFunctionX=s||i||u,this._positionFunctionY=c||i||u,this._positionFunctionZ=h||i||u,this.connectLastAndFirstAnchor=r||!1,this._invertedLightness=e||!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 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<t;o++){let i=[this.anchorPoints[o],this.anchorPoints[(o+1)%this.anchorPoints.length]];this._anchorPairs.push(i)}this.points=this._anchorPairs.map((o,i)=>{let s=o[0]?o[0].position:[0,0,0],c=o[1]?o[1].position:[0,0,0];return X(s,c,this._numPoints,!!(i%2),this.positionFunctionX,this.positionFunctionY,this.positionFunctionZ).map(h=>new b({xyz:h,invertedLightness:this._invertedLightness}))})}addAnchorPoint({xyz:t,color:o,insertAtIndex:i}){let s=new b({xyz:t,color:o,invertedLightness:this._invertedLightness});return i?this.anchorPoints.splice(i,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");let i;if(o!==void 0?i=o:t&&(i=this.anchorPoints.indexOf(t)),i>-1&&i<this.anchorPoints.length)this.anchorPoints.splice(i,1),this.updateAnchorPairs();else throw new Error("Point not found")}updateAnchorPoint({point:t,pointIndex:o,xyz:i,color:s}){if(o&&(t=this.anchorPoints[o]),!t)throw new Error("Must provide a point or pointIndex");if(!i&&!s)throw new Error("Must provide a new xyz position or color");return i&&(t.position=i),s&&(t.hsl=s),this.updateAnchorPairs(),t}getClosestAnchorPoint({xyz:t,hsl:o,maxDistance:i=1}){if(!t&&!o)throw new Error("Must provide a xyz or hsl");let s;t?s=this.anchorPoints.map(r=>g(r.position,t)):o&&(s=this.anchorPoints.map(r=>g(r.hsl,o,!0)));let c=Math.min(...s);if(c>i)return null;let h=s.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.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&&t.pop(),t}cssColors(t="hsl"){let o={hsl:s=>s.hslCSS,oklch:s=>s.oklchCSS,lch:s=>s.lchCSS},i=this.flattenedPoints.map(o[t]);return this.connectLastAndFirstAnchor&&i.pop(),i}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()}},{p5:P}=globalThis;if(P){console.info("p5 detected, adding poline to p5 prototype");let n=new f;P.prototype.poline=n;let t=()=>n.colors.map(o=>`hsl(${Math.round(o[0])},${o[1]*100}%,${o[2]*100}%)`);P.prototype.polineColors=t,P.prototype.registerMethod("polineColors",P.prototype.polineColors),globalThis.poline=n,globalThis.polineColors=t} | ||
| "use strict";var F=Object.defineProperty;var v=Object.getOwnPropertyDescriptor;var V=Object.getOwnPropertyNames;var x=Object.prototype.hasOwnProperty;var l=Math.pow;var A=(n,t)=>{for(var o in t)F(n,o,{get:t[o],enumerable:!0})},w=(n,t,o,i)=>{if(t&&typeof t=="object"||typeof t=="function")for(let s of V(t))!x.call(n,s)&&s!==o&&F(n,s,{get:()=>t[s],enumerable:!(i=v(t,s))||i.enumerable});return n};var y=n=>w(F({},"__esModule",{value:!0}),n);var T={};A(T,{ColorPoint:()=>d,Poline:()=>f,hslToPoint:()=>m,pointToHSL:()=>g,positionFunctions:()=>q,randomHSLPair:()=>C,randomHSLTriple:()=>L});module.exports=y(T);var g=(n,t)=>{let[o,i,s]=n,c=.5,h=.5,e=Math.atan2(i-h,o-c)*(180/Math.PI);e=(360+e)%360;let a=s,p=Math.sqrt(Math.pow(i-h,2)+Math.pow(o-c,2))/c;return[e,a,t?1-p:p]},m=(n,t)=>{let[o,i,s]=n,c=.5,h=.5,r=o/(180/Math.PI),e=(t?1-s:s)*c,a=c+e*Math.cos(r),P=h+e*Math.sin(r);return[a,P,i]},C=(n=Math.random()*360,t=[Math.random(),Math.random()],o=[.75+Math.random()*.2,.3+Math.random()*.2])=>[[n,t[0],o[0]],[(n+60+Math.random()*180)%360,t[1],o[1]]],L=(n=Math.random()*360,t=[Math.random(),Math.random(),Math.random()],o=[.75+Math.random()*.2,Math.random()*.2,.75+Math.random()*.2])=>[[n,t[0],o[0]],[(n+60+Math.random()*180)%360,t[1],o[1]],[(n+60+Math.random()*180)%360,t[2],o[2]]],S=(n,t,o,i=!1,s=(r,e)=>e?1-r:r,c=(r,e)=>e?1-r:r,h=(r,e)=>e?1-r:r)=>{let r=s(n,i),e=c(n,i),a=h(n,i),P=(1-r)*t[0]+r*o[0],p=(1-e)*t[1]+e*o[1],M=(1-a)*t[2]+a*o[2];return[P,p,M]},X=(n,t,o=4,i=!1,s=(r,e)=>e?1-r:r,c=(r,e)=>e?1-r:r,h=(r,e)=>e?1-r:r)=>{let r=[];for(let e=0;e<o;e++){let[a,P,p]=S(e/(o-1),n,t,i,s,c,h);r.push([a,P,p])}return r},Y=n=>n,Z=(n,t=!1)=>t?1-l(1-n,2):l(n,2),z=(n,t=!1)=>t?1-l(1-n,3):l(n,3),E=(n,t=!1)=>t?1-l(1-n,4):l(n,4),$=(n,t=!1)=>t?1-l(1-n,5):l(n,5),u=(n,t=!1)=>t?1-Math.sin((1-n)*Math.PI/2):Math.sin(n*Math.PI/2),k=(n,t=!1)=>t?1-Math.asin(1-n)/(Math.PI/2):Math.asin(n)/(Math.PI/2),I=(n,t=!1)=>t?1-Math.sqrt(1-l(n,2)):1-Math.sqrt(1-n),O=n=>l(n,2)*(3-2*n),q={linearPosition:Y,exponentialPosition:Z,quadraticPosition:z,cubicPosition:E,quarticPosition:$,sinusoidalPosition:u,asinusoidalPosition:k,arcPosition:I,smoothStepPosition:O},_=(n,t,o=!1)=>{let i=n[0],s=t[0],c=0;o&&i!==null&&s!==null?(c=Math.min(Math.abs(i-s),360-Math.abs(i-s)),c=c/360):c=i===null||s===null?0:i-s;let h=c,r=n[1]===null||t[1]===null?0:t[1]-n[1],e=n[2]===null||t[2]===null?0:t[2]-n[2];return Math.sqrt(h*h+r*r+e*e)},d=class{constructor({xyz:t,color:o,invertedLightness:i=!1}={}){this.x=0;this.y=0;this.z=0;this.color=[0,0,0];this._invertedLightness=!1;this._invertedLightness=i,this.positionOrColor({xyz:t,color:o,invertedLightness:i})}positionOrColor({xyz:t,color:o,invertedLightness:i=!1}){if(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=g([this.x,this.y,this.z],i)):o&&(this.color=o,[this.x,this.y,this.z]=m(o,i))}set position([t,o,i]){this.x=t,this.y=o,this.z=i,this.color=g([this.x,this.y,this.z],this._invertedLightness)}get position(){return[this.x,this.y,this.z]}set hsl([t,o,i]){this.color=[t,o,i],[this.x,this.y,this.z]=m(this.color,this._invertedLightness)}get hsl(){return this.color}get hslCSS(){let[t,o,i]=this.color;return`hsl(${t.toFixed(2)}, ${(o*100).toFixed(2)}%, ${(i*100).toFixed(2)}%)`}get oklchCSS(){let[t,o,i]=this.color;return`oklch(${(i*100).toFixed(2)}% ${(o*.4).toFixed(3)} ${t.toFixed(2)})`}get lchCSS(){let[t,o,i]=this.color;return`lch(${(i*100).toFixed(2)}% ${(o*150).toFixed(2)} ${t.toFixed(2)})`}shiftHue(t){this.color[0]=(360+(this.color[0]+t))%360,[this.x,this.y,this.z]=m(this.color,this._invertedLightness)}},f=class{constructor({anchorColors:t=C(),numPoints:o=4,positionFunction:i=u,positionFunctionX:s,positionFunctionY:c,positionFunctionZ:h,closedLoop:r,invertedLightness:e}={anchorColors:C(),numPoints:4,positionFunction:u,closedLoop:!1}){this._needsUpdate=!0;this._positionFunctionX=u;this._positionFunctionY=u;this._positionFunctionZ=u;this.connectLastAndFirstAnchor=!1;this._animationFrame=null;this._invertedLightness=!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:e})),this._numPoints=o+2,this._positionFunctionX=s||i||u,this._positionFunctionY=c||i||u,this._positionFunctionZ=h||i||u,this.connectLastAndFirstAnchor=r||!1,this._invertedLightness=e||!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 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<t;o++){let i=[this.anchorPoints[o],this.anchorPoints[(o+1)%this.anchorPoints.length]];this._anchorPairs.push(i)}this.points=this._anchorPairs.map((o,i)=>{let s=o[0]?o[0].position:[0,0,0],c=o[1]?o[1].position:[0,0,0];return X(s,c,this._numPoints,!!(i%2),this.positionFunctionX,this.positionFunctionY,this.positionFunctionZ).map(h=>new d({xyz:h,invertedLightness:this._invertedLightness}))})}addAnchorPoint({xyz:t,color:o,insertAtIndex:i}){let s=new d({xyz:t,color:o,invertedLightness:this._invertedLightness});return i!==void 0?this.anchorPoints.splice(i,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 i;if(o!==void 0?i=o:t&&(i=this.anchorPoints.indexOf(t)),i>-1&&i<this.anchorPoints.length)this.anchorPoints.splice(i,1),this.updateAnchorPairs();else throw new Error("Point not found")}updateAnchorPoint({point:t,pointIndex:o,xyz:i,color:s}){if(o!==void 0&&(t=this.anchorPoints[o]),!t)throw new Error("Must provide a point or pointIndex");if(!i&&!s)throw new Error("Must provide a new xyz position or color");return i&&(t.position=i),s&&(t.hsl=s),this.updateAnchorPairs(),t}getClosestAnchorPoint({xyz:t,hsl:o,maxDistance:i=1}){if(!t&&!o)throw new Error("Must provide a xyz or hsl");let s;t?s=this.anchorPoints.map(r=>_(r.position,t)):o&&(s=this.anchorPoints.map(r=>_(r.hsl,o,!0)));let c=Math.min(...s);if(c>i)return null;let h=s.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.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&&t.pop(),t}cssColors(t="hsl"){let o={hsl:s=>s.hslCSS,oklch:s=>s.oklchCSS,lch:s=>s.lchCSS},i=this.flattenedPoints.map(o[t]);return this.connectLastAndFirstAnchor&&i.pop(),i}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()}},{p5:b}=globalThis;if(b){console.info("p5 detected, adding poline to p5 prototype");let n=new f;b.prototype.poline=n;let t=()=>n.colors.map(o=>`hsl(${Math.round(o[0])},${o[1]*100}%,${o[2]*100}%)`);b.prototype.polineColors=t,b.prototype.registerMethod("polineColors",b.prototype.polineColors),globalThis.poline=n,globalThis.polineColors=t} |
@@ -1,1 +0,1 @@ | ||
| "use strict";var poline=(()=>{var F=Object.defineProperty;var C=Object.getOwnPropertyDescriptor;var V=Object.getOwnPropertyNames;var x=Object.prototype.hasOwnProperty;var a=Math.pow;var A=(n,t)=>{for(var o in t)F(n,o,{get:t[o],enumerable:!0})},y=(n,t,o,i)=>{if(t&&typeof t=="object"||typeof t=="function")for(let s of V(t))!x.call(n,s)&&s!==o&&F(n,s,{get:()=>t[s],enumerable:!(i=C(t,s))||i.enumerable});return n};var L=n=>y(F({},"__esModule",{value:!0}),n);var T={};A(T,{Poline:()=>f,hslToPoint:()=>m,pointToHSL:()=>_,positionFunctions:()=>q,randomHSLPair:()=>M,randomHSLTriple:()=>w});var _=(n,t)=>{let[o,i,s]=n,c=.5,h=.5,e=Math.atan2(i-h,o-c)*(180/Math.PI);e=(360+e)%360;let l=s,p=Math.sqrt(Math.pow(i-h,2)+Math.pow(o-c,2))/c;return[e,l,t?1-p:p]},m=(n,t)=>{let[o,i,s]=n,c=.5,h=.5,r=o/(180/Math.PI),e=(t?1-s:s)*c,l=c+e*Math.cos(r),d=h+e*Math.sin(r);return[l,d,i]},M=(n=Math.random()*360,t=[Math.random(),Math.random()],o=[.75+Math.random()*.2,.3+Math.random()*.2])=>[[n,t[0],o[0]],[(n+60+Math.random()*180)%360,t[1],o[1]]],w=(n=Math.random()*360,t=[Math.random(),Math.random(),Math.random()],o=[.75+Math.random()*.2,Math.random()*.2,.75+Math.random()*.2])=>[[n,t[0],o[0]],[(n+60+Math.random()*180)%360,t[1],o[1]],[(n+60+Math.random()*180)%360,t[2],o[2]]],S=(n,t,o,i=!1,s=(r,e)=>e?1-r:r,c=(r,e)=>e?1-r:r,h=(r,e)=>e?1-r:r)=>{let r=s(n,i),e=c(n,i),l=h(n,i),d=(1-r)*t[0]+r*o[0],p=(1-e)*t[1]+e*o[1],v=(1-l)*t[2]+l*o[2];return[d,p,v]},X=(n,t,o=4,i=!1,s=(r,e)=>e?1-r:r,c=(r,e)=>e?1-r:r,h=(r,e)=>e?1-r:r)=>{let r=[];for(let e=0;e<o;e++){let[l,d,p]=S(e/(o-1),n,t,i,s,c,h);r.push([l,d,p])}return r},z=n=>n,Y=(n,t=!1)=>t?1-a(1-n,2):a(n,2),Z=(n,t=!1)=>t?1-a(1-n,3):a(n,3),$=(n,t=!1)=>t?1-a(1-n,4):a(n,4),E=(n,t=!1)=>t?1-a(1-n,5):a(n,5),u=(n,t=!1)=>t?1-Math.sin((1-n)*Math.PI/2):Math.sin(n*Math.PI/2),I=(n,t=!1)=>t?1-Math.asin(1-n)/(Math.PI/2):Math.asin(n)/(Math.PI/2),O=(n,t=!1)=>t?Math.sqrt(1-a(1-n,2)):1-Math.sqrt(1-n),k=n=>a(n,2)*(3-2*n),q={linearPosition:z,exponentialPosition:Y,quadraticPosition:Z,cubicPosition:$,quarticPosition:E,sinusoidalPosition:u,asinusoidalPosition:I,arcPosition:O,smoothStepPosition:k},g=(n,t,o=!1)=>{let i=n[0],s=t[0],c=0;o&&i!==null&&s!==null?(c=Math.min(Math.abs(i-s),360-Math.abs(i-s)),c=c/360):c=i===null||s===null?0:i-s;let h=c,r=n[1]===null||t[1]===null?0:t[1]-n[1],e=n[2]===null||t[2]===null?0:t[2]-n[2];return Math.sqrt(h*h+r*r+e*e)},b=class{constructor({xyz:t,color:o,invertedLightness:i}={}){this.x=0;this.y=0;this.z=0;this.color=[0,0,0];this._invertedLightness=!1;this._invertedLightness=i||!1,this.positionOrColor({xyz:t,color:o,invertedLightness:i})}positionOrColor({xyz:t,color:o,invertedLightness:i}){if(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=_([this.x,this.y,this.z],i||!1)):o&&(this.color=o,[this.x,this.y,this.z]=m(o,i||!1))}set position([t,o,i]){this.x=t,this.y=o,this.z=i,this.color=_([this.x,this.y,this.z],this._invertedLightness)}get position(){return[this.x,this.y,this.z]}set hsl([t,o,i]){this.color=[t,o,i],[this.x,this.y,this.z]=m(this.color,this._invertedLightness)}get hsl(){return this.color}get hslCSS(){let[t,o,i]=this.color;return`hsl(${t.toFixed(2)}, ${(o*100).toFixed(2)}%, ${(i*100).toFixed(2)}%)`}get oklchCSS(){let[t,o,i]=this.color;return`oklch(${(i*100).toFixed(2)}% ${(o*.4).toFixed(3)} ${t.toFixed(2)})`}get lchCSS(){let[t,o,i]=this.color;return`lch(${(i*100).toFixed(2)}% ${(o*150).toFixed(2)} ${t.toFixed(2)})`}shiftHue(t){this.color[0]=(360+(this.color[0]+t))%360,[this.x,this.y,this.z]=m(this.color,this._invertedLightness)}},f=class{constructor({anchorColors:t=M(),numPoints:o=4,positionFunction:i=u,positionFunctionX:s,positionFunctionY:c,positionFunctionZ:h,closedLoop:r,invertedLightness:e}={anchorColors:M(),numPoints:4,positionFunction:u,closedLoop:!1}){this._needsUpdate=!0;this._positionFunctionX=u;this._positionFunctionY=u;this._positionFunctionZ=u;this.connectLastAndFirstAnchor=!1;this._animationFrame=null;this._invertedLightness=!1;if(!t||t.length<2)throw new Error("Must have at least two anchor colors");this._anchorPoints=t.map(l=>new b({color:l,invertedLightness:e})),this._numPoints=o+2,this._positionFunctionX=s||i||u,this._positionFunctionY=c||i||u,this._positionFunctionZ=h||i||u,this.connectLastAndFirstAnchor=r||!1,this._invertedLightness=e||!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 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<t;o++){let i=[this.anchorPoints[o],this.anchorPoints[(o+1)%this.anchorPoints.length]];this._anchorPairs.push(i)}this.points=this._anchorPairs.map((o,i)=>{let s=o[0]?o[0].position:[0,0,0],c=o[1]?o[1].position:[0,0,0];return X(s,c,this._numPoints,!!(i%2),this.positionFunctionX,this.positionFunctionY,this.positionFunctionZ).map(h=>new b({xyz:h,invertedLightness:this._invertedLightness}))})}addAnchorPoint({xyz:t,color:o,insertAtIndex:i}){let s=new b({xyz:t,color:o,invertedLightness:this._invertedLightness});return i?this.anchorPoints.splice(i,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");let i;if(o!==void 0?i=o:t&&(i=this.anchorPoints.indexOf(t)),i>-1&&i<this.anchorPoints.length)this.anchorPoints.splice(i,1),this.updateAnchorPairs();else throw new Error("Point not found")}updateAnchorPoint({point:t,pointIndex:o,xyz:i,color:s}){if(o&&(t=this.anchorPoints[o]),!t)throw new Error("Must provide a point or pointIndex");if(!i&&!s)throw new Error("Must provide a new xyz position or color");return i&&(t.position=i),s&&(t.hsl=s),this.updateAnchorPairs(),t}getClosestAnchorPoint({xyz:t,hsl:o,maxDistance:i=1}){if(!t&&!o)throw new Error("Must provide a xyz or hsl");let s;t?s=this.anchorPoints.map(r=>g(r.position,t)):o&&(s=this.anchorPoints.map(r=>g(r.hsl,o,!0)));let c=Math.min(...s);if(c>i)return null;let h=s.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.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&&t.pop(),t}cssColors(t="hsl"){let o={hsl:s=>s.hslCSS,oklch:s=>s.oklchCSS,lch:s=>s.lchCSS},i=this.flattenedPoints.map(o[t]);return this.connectLastAndFirstAnchor&&i.pop(),i}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()}},{p5:P}=globalThis;if(P){console.info("p5 detected, adding poline to p5 prototype");let n=new f;P.prototype.poline=n;let t=()=>n.colors.map(o=>`hsl(${Math.round(o[0])},${o[1]*100}%,${o[2]*100}%)`);P.prototype.polineColors=t,P.prototype.registerMethod("polineColors",P.prototype.polineColors),globalThis.poline=n,globalThis.polineColors=t}return L(T);})(); | ||
| "use strict";var poline=(()=>{var F=Object.defineProperty;var v=Object.getOwnPropertyDescriptor;var V=Object.getOwnPropertyNames;var x=Object.prototype.hasOwnProperty;var l=Math.pow;var A=(n,t)=>{for(var o in t)F(n,o,{get:t[o],enumerable:!0})},w=(n,t,o,i)=>{if(t&&typeof t=="object"||typeof t=="function")for(let s of V(t))!x.call(n,s)&&s!==o&&F(n,s,{get:()=>t[s],enumerable:!(i=v(t,s))||i.enumerable});return n};var y=n=>w(F({},"__esModule",{value:!0}),n);var T={};A(T,{ColorPoint:()=>d,Poline:()=>f,hslToPoint:()=>m,pointToHSL:()=>g,positionFunctions:()=>q,randomHSLPair:()=>C,randomHSLTriple:()=>L});var g=(n,t)=>{let[o,i,s]=n,c=.5,h=.5,e=Math.atan2(i-h,o-c)*(180/Math.PI);e=(360+e)%360;let a=s,p=Math.sqrt(Math.pow(i-h,2)+Math.pow(o-c,2))/c;return[e,a,t?1-p:p]},m=(n,t)=>{let[o,i,s]=n,c=.5,h=.5,r=o/(180/Math.PI),e=(t?1-s:s)*c,a=c+e*Math.cos(r),P=h+e*Math.sin(r);return[a,P,i]},C=(n=Math.random()*360,t=[Math.random(),Math.random()],o=[.75+Math.random()*.2,.3+Math.random()*.2])=>[[n,t[0],o[0]],[(n+60+Math.random()*180)%360,t[1],o[1]]],L=(n=Math.random()*360,t=[Math.random(),Math.random(),Math.random()],o=[.75+Math.random()*.2,Math.random()*.2,.75+Math.random()*.2])=>[[n,t[0],o[0]],[(n+60+Math.random()*180)%360,t[1],o[1]],[(n+60+Math.random()*180)%360,t[2],o[2]]],S=(n,t,o,i=!1,s=(r,e)=>e?1-r:r,c=(r,e)=>e?1-r:r,h=(r,e)=>e?1-r:r)=>{let r=s(n,i),e=c(n,i),a=h(n,i),P=(1-r)*t[0]+r*o[0],p=(1-e)*t[1]+e*o[1],M=(1-a)*t[2]+a*o[2];return[P,p,M]},X=(n,t,o=4,i=!1,s=(r,e)=>e?1-r:r,c=(r,e)=>e?1-r:r,h=(r,e)=>e?1-r:r)=>{let r=[];for(let e=0;e<o;e++){let[a,P,p]=S(e/(o-1),n,t,i,s,c,h);r.push([a,P,p])}return r},Y=n=>n,Z=(n,t=!1)=>t?1-l(1-n,2):l(n,2),z=(n,t=!1)=>t?1-l(1-n,3):l(n,3),E=(n,t=!1)=>t?1-l(1-n,4):l(n,4),$=(n,t=!1)=>t?1-l(1-n,5):l(n,5),u=(n,t=!1)=>t?1-Math.sin((1-n)*Math.PI/2):Math.sin(n*Math.PI/2),k=(n,t=!1)=>t?1-Math.asin(1-n)/(Math.PI/2):Math.asin(n)/(Math.PI/2),I=(n,t=!1)=>t?1-Math.sqrt(1-l(n,2)):1-Math.sqrt(1-n),O=n=>l(n,2)*(3-2*n),q={linearPosition:Y,exponentialPosition:Z,quadraticPosition:z,cubicPosition:E,quarticPosition:$,sinusoidalPosition:u,asinusoidalPosition:k,arcPosition:I,smoothStepPosition:O},_=(n,t,o=!1)=>{let i=n[0],s=t[0],c=0;o&&i!==null&&s!==null?(c=Math.min(Math.abs(i-s),360-Math.abs(i-s)),c=c/360):c=i===null||s===null?0:i-s;let h=c,r=n[1]===null||t[1]===null?0:t[1]-n[1],e=n[2]===null||t[2]===null?0:t[2]-n[2];return Math.sqrt(h*h+r*r+e*e)},d=class{constructor({xyz:t,color:o,invertedLightness:i=!1}={}){this.x=0;this.y=0;this.z=0;this.color=[0,0,0];this._invertedLightness=!1;this._invertedLightness=i,this.positionOrColor({xyz:t,color:o,invertedLightness:i})}positionOrColor({xyz:t,color:o,invertedLightness:i=!1}){if(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=g([this.x,this.y,this.z],i)):o&&(this.color=o,[this.x,this.y,this.z]=m(o,i))}set position([t,o,i]){this.x=t,this.y=o,this.z=i,this.color=g([this.x,this.y,this.z],this._invertedLightness)}get position(){return[this.x,this.y,this.z]}set hsl([t,o,i]){this.color=[t,o,i],[this.x,this.y,this.z]=m(this.color,this._invertedLightness)}get hsl(){return this.color}get hslCSS(){let[t,o,i]=this.color;return`hsl(${t.toFixed(2)}, ${(o*100).toFixed(2)}%, ${(i*100).toFixed(2)}%)`}get oklchCSS(){let[t,o,i]=this.color;return`oklch(${(i*100).toFixed(2)}% ${(o*.4).toFixed(3)} ${t.toFixed(2)})`}get lchCSS(){let[t,o,i]=this.color;return`lch(${(i*100).toFixed(2)}% ${(o*150).toFixed(2)} ${t.toFixed(2)})`}shiftHue(t){this.color[0]=(360+(this.color[0]+t))%360,[this.x,this.y,this.z]=m(this.color,this._invertedLightness)}},f=class{constructor({anchorColors:t=C(),numPoints:o=4,positionFunction:i=u,positionFunctionX:s,positionFunctionY:c,positionFunctionZ:h,closedLoop:r,invertedLightness:e}={anchorColors:C(),numPoints:4,positionFunction:u,closedLoop:!1}){this._needsUpdate=!0;this._positionFunctionX=u;this._positionFunctionY=u;this._positionFunctionZ=u;this.connectLastAndFirstAnchor=!1;this._animationFrame=null;this._invertedLightness=!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:e})),this._numPoints=o+2,this._positionFunctionX=s||i||u,this._positionFunctionY=c||i||u,this._positionFunctionZ=h||i||u,this.connectLastAndFirstAnchor=r||!1,this._invertedLightness=e||!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 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<t;o++){let i=[this.anchorPoints[o],this.anchorPoints[(o+1)%this.anchorPoints.length]];this._anchorPairs.push(i)}this.points=this._anchorPairs.map((o,i)=>{let s=o[0]?o[0].position:[0,0,0],c=o[1]?o[1].position:[0,0,0];return X(s,c,this._numPoints,!!(i%2),this.positionFunctionX,this.positionFunctionY,this.positionFunctionZ).map(h=>new d({xyz:h,invertedLightness:this._invertedLightness}))})}addAnchorPoint({xyz:t,color:o,insertAtIndex:i}){let s=new d({xyz:t,color:o,invertedLightness:this._invertedLightness});return i!==void 0?this.anchorPoints.splice(i,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 i;if(o!==void 0?i=o:t&&(i=this.anchorPoints.indexOf(t)),i>-1&&i<this.anchorPoints.length)this.anchorPoints.splice(i,1),this.updateAnchorPairs();else throw new Error("Point not found")}updateAnchorPoint({point:t,pointIndex:o,xyz:i,color:s}){if(o!==void 0&&(t=this.anchorPoints[o]),!t)throw new Error("Must provide a point or pointIndex");if(!i&&!s)throw new Error("Must provide a new xyz position or color");return i&&(t.position=i),s&&(t.hsl=s),this.updateAnchorPairs(),t}getClosestAnchorPoint({xyz:t,hsl:o,maxDistance:i=1}){if(!t&&!o)throw new Error("Must provide a xyz or hsl");let s;t?s=this.anchorPoints.map(r=>_(r.position,t)):o&&(s=this.anchorPoints.map(r=>_(r.hsl,o,!0)));let c=Math.min(...s);if(c>i)return null;let h=s.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.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&&t.pop(),t}cssColors(t="hsl"){let o={hsl:s=>s.hslCSS,oklch:s=>s.oklchCSS,lch:s=>s.lchCSS},i=this.flattenedPoints.map(o[t]);return this.connectLastAndFirstAnchor&&i.pop(),i}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()}},{p5:b}=globalThis;if(b){console.info("p5 detected, adding poline to p5 prototype");let n=new f;b.prototype.poline=n;let t=()=>n.colors.map(o=>`hsl(${Math.round(o[0])},${o[1]*100}%,${o[2]*100}%)`);b.prototype.polineColors=t,b.prototype.registerMethod("polineColors",b.prototype.polineColors),globalThis.poline=n,globalThis.polineColors=t}return y(T);})(); |
@@ -1,1 +0,1 @@ | ||
| var f=(i,t)=>{let[o,n,s]=i,c=.5,h=.5,e=Math.atan2(n-h,o-c)*(180/Math.PI);e=(360+e)%360;let a=s,u=Math.sqrt(Math.pow(n-h,2)+Math.pow(o-c,2))/c;return[e,a,t?1-u:u]},b=(i,t)=>{let[o,n,s]=i,c=.5,h=.5,r=o/(180/Math.PI),e=(t?1-s:s)*c,a=c+e*Math.cos(r),p=h+e*Math.sin(r);return[a,p,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]]],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]]],g=(i,t,o,n=!1,s=(r,e)=>e?1-r:r,c=(r,e)=>e?1-r:r,h=(r,e)=>e?1-r:r)=>{let r=s(i,n),e=c(i,n),a=h(i,n),p=(1-r)*t[0]+r*o[0],u=(1-e)*t[1]+e*o[1],M=(1-a)*t[2]+a*o[2];return[p,u,M]},v=(i,t,o=4,n=!1,s=(r,e)=>e?1-r:r,c=(r,e)=>e?1-r:r,h=(r,e)=>e?1-r:r)=>{let r=[];for(let e=0;e<o;e++){let[a,p,u]=g(e/(o-1),i,t,n,s,c,h);r.push([a,p,u])}return r},C=i=>i,V=(i,t=!1)=>t?1-(1-i)**2:i**2,x=(i,t=!1)=>t?1-(1-i)**3:i**3,A=(i,t=!1)=>t?1-(1-i)**4:i**4,y=(i,t=!1)=>t?1-(1-i)**5:i**5,l=(i,t=!1)=>t?1-Math.sin((1-i)*Math.PI/2):Math.sin(i*Math.PI/2),L=(i,t=!1)=>t?1-Math.asin(1-i)/(Math.PI/2):Math.asin(i)/(Math.PI/2),w=(i,t=!1)=>t?Math.sqrt(1-(1-i)**2):1-Math.sqrt(1-i),S=i=>i**2*(3-2*i),z={linearPosition:C,exponentialPosition:V,quadraticPosition:x,cubicPosition:A,quarticPosition:y,sinusoidalPosition:l,asinusoidalPosition:L,arcPosition:w,smoothStepPosition:S},_=(i,t,o=!1)=>{let n=i[0],s=t[0],c=0;o&&n!==null&&s!==null?(c=Math.min(Math.abs(n-s),360-Math.abs(n-s)),c=c/360):c=n===null||s===null?0:n-s;let h=c,r=i[1]===null||t[1]===null?0:t[1]-i[1],e=i[2]===null||t[2]===null?0:t[2]-i[2];return Math.sqrt(h*h+r*r+e*e)},P=class{constructor({xyz:t,color:o,invertedLightness:n}={}){this.x=0;this.y=0;this.z=0;this.color=[0,0,0];this._invertedLightness=!1;this._invertedLightness=n||!1,this.positionOrColor({xyz:t,color:o,invertedLightness:n})}positionOrColor({xyz:t,color:o,invertedLightness:n}){if(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=f([this.x,this.y,this.z],n||!1)):o&&(this.color=o,[this.x,this.y,this.z]=b(o,n||!1))}set position([t,o,n]){this.x=t,this.y=o,this.z=n,this.color=f([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]=b(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)})`}shiftHue(t){this.color[0]=(360+(this.color[0]+t))%360,[this.x,this.y,this.z]=b(this.color,this._invertedLightness)}},m=class{constructor({anchorColors:t=F(),numPoints:o=4,positionFunction:n=l,positionFunctionX:s,positionFunctionY:c,positionFunctionZ:h,closedLoop:r,invertedLightness:e}={anchorColors:F(),numPoints:4,positionFunction:l,closedLoop:!1}){this._needsUpdate=!0;this._positionFunctionX=l;this._positionFunctionY=l;this._positionFunctionZ=l;this.connectLastAndFirstAnchor=!1;this._animationFrame=null;this._invertedLightness=!1;if(!t||t.length<2)throw new Error("Must have at least two anchor colors");this._anchorPoints=t.map(a=>new P({color:a,invertedLightness:e})),this._numPoints=o+2,this._positionFunctionX=s||n||l,this._positionFunctionY=c||n||l,this._positionFunctionZ=h||n||l,this.connectLastAndFirstAnchor=r||!1,this._invertedLightness=e||!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 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<t;o++){let n=[this.anchorPoints[o],this.anchorPoints[(o+1)%this.anchorPoints.length]];this._anchorPairs.push(n)}this.points=this._anchorPairs.map((o,n)=>{let s=o[0]?o[0].position:[0,0,0],c=o[1]?o[1].position:[0,0,0];return v(s,c,this._numPoints,!!(n%2),this.positionFunctionX,this.positionFunctionY,this.positionFunctionZ).map(h=>new P({xyz:h,invertedLightness:this._invertedLightness}))})}addAnchorPoint({xyz:t,color:o,insertAtIndex:n}){let s=new P({xyz:t,color:o,invertedLightness:this._invertedLightness});return n?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");let n;if(o!==void 0?n=o:t&&(n=this.anchorPoints.indexOf(t)),n>-1&&n<this.anchorPoints.length)this.anchorPoints.splice(n,1),this.updateAnchorPairs();else throw new Error("Point not found")}updateAnchorPoint({point:t,pointIndex:o,xyz:n,color:s}){if(o&&(t=this.anchorPoints[o]),!t)throw new Error("Must provide a point or pointIndex");if(!n&&!s)throw new Error("Must provide a new xyz position or color");return n&&(t.position=n),s&&(t.hsl=s),this.updateAnchorPairs(),t}getClosestAnchorPoint({xyz:t,hsl:o,maxDistance:n=1}){if(!t&&!o)throw new Error("Must provide a xyz or hsl");let s;t?s=this.anchorPoints.map(r=>_(r.position,t)):o&&(s=this.anchorPoints.map(r=>_(r.hsl,o,!0)));let c=Math.min(...s);if(c>n)return null;let h=s.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.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&&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()}},{p5:d}=globalThis;if(d){console.info("p5 detected, adding poline to p5 prototype");let i=new m;d.prototype.poline=i;let t=()=>i.colors.map(o=>`hsl(${Math.round(o[0])},${o[1]*100}%,${o[2]*100}%)`);d.prototype.polineColors=t,d.prototype.registerMethod("polineColors",d.prototype.polineColors),globalThis.poline=i,globalThis.polineColors=t}export{m as Poline,b as hslToPoint,f as pointToHSL,z as positionFunctions,F as randomHSLPair,X as randomHSLTriple}; | ||
| var f=(i,t)=>{let[o,n,s]=i,c=.5,h=.5,e=Math.atan2(n-h,o-c)*(180/Math.PI);e=(360+e)%360;let l=s,u=Math.sqrt(Math.pow(n-h,2)+Math.pow(o-c,2))/c;return[e,l,t?1-u:u]},b=(i,t)=>{let[o,n,s]=i,c=.5,h=.5,r=o/(180/Math.PI),e=(t?1-s:s)*c,l=c+e*Math.cos(r),p=h+e*Math.sin(r);return[l,p,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]]],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]]],_=(i,t,o,n=!1,s=(r,e)=>e?1-r:r,c=(r,e)=>e?1-r:r,h=(r,e)=>e?1-r:r)=>{let r=s(i,n),e=c(i,n),l=h(i,n),p=(1-r)*t[0]+r*o[0],u=(1-e)*t[1]+e*o[1],C=(1-l)*t[2]+l*o[2];return[p,u,C]},M=(i,t,o=4,n=!1,s=(r,e)=>e?1-r:r,c=(r,e)=>e?1-r:r,h=(r,e)=>e?1-r:r)=>{let r=[];for(let e=0;e<o;e++){let[l,p,u]=_(e/(o-1),i,t,n,s,c,h);r.push([l,p,u])}return r},v=i=>i,V=(i,t=!1)=>t?1-(1-i)**2:i**2,x=(i,t=!1)=>t?1-(1-i)**3:i**3,A=(i,t=!1)=>t?1-(1-i)**4:i**4,w=(i,t=!1)=>t?1-(1-i)**5:i**5,a=(i,t=!1)=>t?1-Math.sin((1-i)*Math.PI/2):Math.sin(i*Math.PI/2),y=(i,t=!1)=>t?1-Math.asin(1-i)/(Math.PI/2):Math.asin(i)/(Math.PI/2),L=(i,t=!1)=>t?1-Math.sqrt(1-i**2):1-Math.sqrt(1-i),S=i=>i**2*(3-2*i),Y={linearPosition:v,exponentialPosition:V,quadraticPosition:x,cubicPosition:A,quarticPosition:w,sinusoidalPosition:a,asinusoidalPosition:y,arcPosition:L,smoothStepPosition:S},g=(i,t,o=!1)=>{let n=i[0],s=t[0],c=0;o&&n!==null&&s!==null?(c=Math.min(Math.abs(n-s),360-Math.abs(n-s)),c=c/360):c=n===null||s===null?0:n-s;let h=c,r=i[1]===null||t[1]===null?0:t[1]-i[1],e=i[2]===null||t[2]===null?0:t[2]-i[2];return Math.sqrt(h*h+r*r+e*e)},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(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=f([this.x,this.y,this.z],n)):o&&(this.color=o,[this.x,this.y,this.z]=b(o,n))}set position([t,o,n]){this.x=t,this.y=o,this.z=n,this.color=f([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]=b(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)})`}shiftHue(t){this.color[0]=(360+(this.color[0]+t))%360,[this.x,this.y,this.z]=b(this.color,this._invertedLightness)}},m=class{constructor({anchorColors:t=F(),numPoints:o=4,positionFunction:n=a,positionFunctionX:s,positionFunctionY:c,positionFunctionZ:h,closedLoop:r,invertedLightness:e}={anchorColors:F(),numPoints:4,positionFunction:a,closedLoop:!1}){this._needsUpdate=!0;this._positionFunctionX=a;this._positionFunctionY=a;this._positionFunctionZ=a;this.connectLastAndFirstAnchor=!1;this._animationFrame=null;this._invertedLightness=!1;if(!t||t.length<2)throw new Error("Must have at least two anchor colors");this._anchorPoints=t.map(l=>new d({color:l,invertedLightness:e})),this._numPoints=o+2,this._positionFunctionX=s||n||a,this._positionFunctionY=c||n||a,this._positionFunctionZ=h||n||a,this.connectLastAndFirstAnchor=r||!1,this._invertedLightness=e||!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 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<t;o++){let n=[this.anchorPoints[o],this.anchorPoints[(o+1)%this.anchorPoints.length]];this._anchorPairs.push(n)}this.points=this._anchorPairs.map((o,n)=>{let s=o[0]?o[0].position:[0,0,0],c=o[1]?o[1].position:[0,0,0];return M(s,c,this._numPoints,!!(n%2),this.positionFunctionX,this.positionFunctionY,this.positionFunctionZ).map(h=>new d({xyz:h,invertedLightness:this._invertedLightness}))})}addAnchorPoint({xyz:t,color:o,insertAtIndex:n}){let s=new d({xyz:t,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&&n<this.anchorPoints.length)this.anchorPoints.splice(n,1),this.updateAnchorPairs();else throw new Error("Point not found")}updateAnchorPoint({point:t,pointIndex:o,xyz:n,color:s}){if(o!==void 0&&(t=this.anchorPoints[o]),!t)throw new Error("Must provide a point or pointIndex");if(!n&&!s)throw new Error("Must provide a new xyz position or color");return n&&(t.position=n),s&&(t.hsl=s),this.updateAnchorPairs(),t}getClosestAnchorPoint({xyz:t,hsl:o,maxDistance:n=1}){if(!t&&!o)throw new Error("Must provide a xyz or hsl");let s;t?s=this.anchorPoints.map(r=>g(r.position,t)):o&&(s=this.anchorPoints.map(r=>g(r.hsl,o,!0)));let c=Math.min(...s);if(c>n)return null;let h=s.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.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&&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()}},{p5:P}=globalThis;if(P){console.info("p5 detected, adding poline to p5 prototype");let i=new m;P.prototype.poline=i;let t=()=>i.colors.map(o=>`hsl(${Math.round(o[0])},${o[1]*100}%,${o[2]*100}%)`);P.prototype.polineColors=t,P.prototype.registerMethod("polineColors",P.prototype.polineColors),globalThis.poline=i,globalThis.polineColors=t}export{d as ColorPoint,m as Poline,b as hslToPoint,f as pointToHSL,Y as positionFunctions,F as randomHSLPair,X as randomHSLTriple}; |
+48
-21
@@ -0,1 +1,3 @@ | ||
| var __pow = Math.pow; | ||
| // src/index.ts | ||
@@ -68,23 +70,23 @@ var pointToHSL = (xyz, invertedLightness) => { | ||
| if (reverse) { | ||
| return 1 - (1 - t) ** 2; | ||
| return 1 - __pow(1 - t, 2); | ||
| } | ||
| return t ** 2; | ||
| return __pow(t, 2); | ||
| }; | ||
| var quadraticPosition = (t, reverse = false) => { | ||
| if (reverse) { | ||
| return 1 - (1 - t) ** 3; | ||
| return 1 - __pow(1 - t, 3); | ||
| } | ||
| return t ** 3; | ||
| return __pow(t, 3); | ||
| }; | ||
| var cubicPosition = (t, reverse = false) => { | ||
| if (reverse) { | ||
| return 1 - (1 - t) ** 4; | ||
| return 1 - __pow(1 - t, 4); | ||
| } | ||
| return t ** 4; | ||
| return __pow(t, 4); | ||
| }; | ||
| var quarticPosition = (t, reverse = false) => { | ||
| if (reverse) { | ||
| return 1 - (1 - t) ** 5; | ||
| return 1 - __pow(1 - t, 5); | ||
| } | ||
| return t ** 5; | ||
| return __pow(t, 5); | ||
| }; | ||
@@ -105,3 +107,3 @@ var sinusoidalPosition = (t, reverse = false) => { | ||
| if (reverse) { | ||
| return Math.sqrt(1 - (1 - t) ** 2); | ||
| return 1 - Math.sqrt(1 - __pow(t, 2)); | ||
| } | ||
@@ -111,3 +113,3 @@ return 1 - Math.sqrt(1 - t); | ||
| var smoothStepPosition = (t) => { | ||
| return t ** 2 * (3 - 2 * t); | ||
| return __pow(t, 2) * (3 - 2 * t); | ||
| }; | ||
@@ -141,3 +143,7 @@ var positionFunctions = { | ||
| var ColorPoint = class { | ||
| constructor({ xyz, color, invertedLightness } = {}) { | ||
| constructor({ | ||
| xyz, | ||
| color, | ||
| invertedLightness = false | ||
| } = {}) { | ||
| this.x = 0; | ||
@@ -148,7 +154,11 @@ this.y = 0; | ||
| this._invertedLightness = false; | ||
| this._invertedLightness = invertedLightness || false; | ||
| this._invertedLightness = invertedLightness; | ||
| this.positionOrColor({ xyz, color, invertedLightness }); | ||
| } | ||
| positionOrColor({ xyz, color, invertedLightness }) { | ||
| if (xyz && color) { | ||
| positionOrColor({ | ||
| xyz, | ||
| color, | ||
| invertedLightness = false | ||
| }) { | ||
| if (xyz && color || !xyz && !color) { | ||
| throw new Error("Point must be initialized with either x,y,z or hsl"); | ||
@@ -159,9 +169,6 @@ } else if (xyz) { | ||
| this.z = xyz[2]; | ||
| this.color = pointToHSL( | ||
| [this.x, this.y, this.z], | ||
| invertedLightness || false | ||
| ); | ||
| this.color = pointToHSL([this.x, this.y, this.z], invertedLightness); | ||
| } else if (color) { | ||
| this.color = color; | ||
| [this.x, this.y, this.z] = hslToPoint(color, invertedLightness || false); | ||
| [this.x, this.y, this.z] = hslToPoint(color, invertedLightness); | ||
| } | ||
@@ -356,3 +363,3 @@ } | ||
| }); | ||
| if (insertAtIndex) { | ||
| if (insertAtIndex !== void 0) { | ||
| this.anchorPoints.splice(insertAtIndex, 0, newAnchor); | ||
@@ -372,2 +379,5 @@ } else { | ||
| } | ||
| if (this.anchorPoints.length < 3) { | ||
| throw new Error("Must have at least two anchor points"); | ||
| } | ||
| let apid; | ||
@@ -392,3 +402,3 @@ if (index !== void 0) { | ||
| }) { | ||
| if (pointIndex) { | ||
| if (pointIndex !== void 0) { | ||
| point = this.anchorPoints[pointIndex]; | ||
@@ -448,2 +458,17 @@ } | ||
| } | ||
| /** | ||
| * Returns a flattened array of all points across all segments, | ||
| * removing duplicated anchor points at segment boundaries. | ||
| * | ||
| * Since anchor points exist at both the end of one segment and | ||
| * the beginning of the next, this method keeps only one instance of each. | ||
| * The filter logic keeps the first point (index 0) and then filters out | ||
| * points whose indices are multiples of the segment size (_numPoints), | ||
| * which are the anchor points at the start of each segment (except the first). | ||
| * | ||
| * This approach ensures we get all unique points in the correct order | ||
| * while avoiding duplicated anchor points. | ||
| * | ||
| * @returns {ColorPoint[]} A flat array of unique ColorPoint instances | ||
| */ | ||
| get flattenedPoints() { | ||
@@ -499,2 +524,3 @@ return this.points.flat().filter((p, i) => i != 0 ? i % this._numPoints : true); | ||
| export { | ||
| ColorPoint, | ||
| Poline, | ||
@@ -507,1 +533,2 @@ hslToPoint, | ||
| }; | ||
| //# sourceMappingURL=index.mjs.map |
| { | ||
| "version": 3, | ||
| "sources": ["../src/index.ts"], | ||
| "sourcesContent": ["/* eslint-disable @typescript-eslint/ban-ts-comment */\nexport type FuncNumberReturn = (arg0: number) => Vector2;\nexport type Vector2 = [number, number];\nexport type Vector3 = [number, ...Vector2];\nexport type PartialVector3 = [number | null, number | null, number | null];\n\n/**\n * Converts the given (x, y, z) coordinate to an HSL color\n * The (x, y) values are used to calculate the hue, while the z value is used as the saturation\n * The lightness value is calculated based on the distance of (x, y) from the center (0.5, 0.5)\n * Returns an array [hue, saturation, lightness]\n * @param xyz:Vector3 [x, y, z] coordinate array in (x, y, z) format (0-1, 0-1, 0-1)\n * @returns [hue, saturation, lightness]: Vector3 color array in HSL format (0-360, 0-1, 0-1)\n * @example\n * pointToHSL(0.5, 0.5, 1) // [0, 1, 0.5]\n * pointToHSL(0.5, 0.5, 0) // [0, 1, 0]\n * pointToHSL(0.5, 0.5, 1) // [0, 1, 1]\n **/\n\nexport const pointToHSL = (\n xyz: Vector3,\n invertedLightness: boolean\n): Vector3 => {\n const [x, y, z] = xyz;\n\n // cy and cx are the center (y and x) values\n const cx = 0.5;\n const cy = 0.5;\n\n // Calculate the angle between the point (x, y) and the center (cx, cy)\n const radians = Math.atan2(y - cy, x - cx);\n\n // Convert the angle to degrees and shift it so that it goes from 0 to 360\n let deg = radians * (180 / Math.PI);\n deg = (360 + deg) % 360;\n\n // The saturation value is taken from the z coordinate\n const s = z;\n\n // Calculate the lightness value based on the distance from the center\n const dist = Math.sqrt(Math.pow(y - cy, 2) + Math.pow(x - cx, 2));\n const l = dist / cx;\n\n // Return the HSL color as an array [hue, saturation, lightness]\n return [deg, s, invertedLightness ? 1 - l : l];\n};\n\n/**\n * Converts the given HSL color to an (x, y, z) coordinate\n * The hue value is used to calculate the (x, y) position, while the saturation value is used as the z coordinate\n * The lightness value is used to calculate the distance from the center (0.5, 0.5)\n * Returns an array [x, y, z]\n * @param hsl:Vector3 [hue, saturation, lightness] color array in HSL format (0-360, 0-1, 0-1)\n * @returns [x, y, z]:Vector3 coordinate array in (x, y, z) format (0-1, 0-1, 0-1)\n * @example\n * hslToPoint([0, 1, 0.5]) // [0.5, 0.5, 1]\n * hslToPoint([0, 1, 0]) // [0.5, 0.5, 1]\n * hslToPoint([0, 1, 1]) // [0.5, 0.5, 1]\n * hslToPoint([0, 0, 0.5]) // [0.5, 0.5, 0]\n **/\nexport const hslToPoint = (\n hsl: Vector3,\n invertedLightness: boolean\n): Vector3 => {\n // Destructure the input array into separate hue, saturation, and lightness values\n const [h, s, l] = hsl;\n // cx and cy are the center (x and y) values\n const cx = 0.5;\n const cy = 0.5;\n // Calculate the angle in radians based on the hue value\n const radians = h / (180 / Math.PI);\n\n // Calculate the distance from the center based on the lightness value\n const dist = (invertedLightness ? 1 - l : l) * cx;\n\n // Calculate the x and y coordinates based on the distance and angle\n const x = cx + dist * Math.cos(radians);\n const y = cy + dist * Math.sin(radians);\n // The z coordinate is equal to the saturation value\n const z = s;\n // Return the (x, y, z) coordinate as an array [x, y, z]\n return [x, y, z];\n};\n\nexport const randomHSLPair = (\n startHue: number = Math.random() * 360,\n saturations: Vector2 = [Math.random(), Math.random()],\n lightnesses: Vector2 = [0.75 + Math.random() * 0.2, 0.3 + Math.random() * 0.2]\n): [Vector3, Vector3] => [\n [startHue, saturations[0], lightnesses[0]],\n [(startHue + 60 + Math.random() * 180) % 360, saturations[1], lightnesses[1]],\n];\n\nexport const randomHSLTriple = (\n startHue: number = Math.random() * 360,\n saturations: Vector3 = [Math.random(), Math.random(), Math.random()],\n lightnesses: Vector3 = [\n 0.75 + Math.random() * 0.2,\n Math.random() * 0.2,\n 0.75 + Math.random() * 0.2,\n ]\n): [Vector3, Vector3, Vector3] => [\n [startHue, saturations[0], lightnesses[0]],\n [(startHue + 60 + Math.random() * 180) % 360, saturations[1], lightnesses[1]],\n [(startHue + 60 + Math.random() * 180) % 360, saturations[2], lightnesses[2]],\n];\n\nconst vectorOnLine = (\n t: number,\n p1: Vector3,\n p2: Vector3,\n invert = false,\n fx = (t: number, invert: boolean): number => (invert ? 1 - t : t),\n fy = (t: number, invert: boolean): number => (invert ? 1 - t : t),\n fz = (t: number, invert: boolean): number => (invert ? 1 - t : t)\n): Vector3 => {\n const tModifiedX = fx(t, invert);\n const tModifiedY = fy(t, invert);\n const tModifiedZ = fz(t, invert);\n const x = (1 - tModifiedX) * p1[0] + tModifiedX * p2[0];\n const y = (1 - tModifiedY) * p1[1] + tModifiedY * p2[1];\n const z = (1 - tModifiedZ) * p1[2] + tModifiedZ * p2[2];\n\n return [x, y, z];\n};\n\nconst vectorsOnLine = (\n p1: Vector3,\n p2: Vector3,\n numPoints = 4,\n invert = false,\n fx = (t: number, invert: boolean): number => (invert ? 1 - t : t),\n fy = (t: number, invert: boolean): number => (invert ? 1 - t : t),\n fz = (t: number, invert: boolean): number => (invert ? 1 - t : t)\n): Vector3[] => {\n const points: Vector3[] = [];\n\n for (let i = 0; i < numPoints; i++) {\n const [x, y, z] = vectorOnLine(\n i / (numPoints - 1),\n p1,\n p2,\n invert,\n fx,\n fy,\n fz\n );\n points.push([x, y, z]);\n }\n\n return points;\n};\n\nexport type PositionFunction = (t: number, reverse?: boolean) => number;\n\nconst linearPosition: PositionFunction = (t: number) => {\n return t;\n};\n\nconst exponentialPosition: PositionFunction = (t: number, reverse = false) => {\n if (reverse) {\n return 1 - (1 - t) ** 2;\n }\n return t ** 2;\n};\n\nconst quadraticPosition: PositionFunction = (t: number, reverse = false) => {\n if (reverse) {\n return 1 - (1 - t) ** 3;\n }\n return t ** 3;\n};\n\nconst cubicPosition: PositionFunction = (t: number, reverse = false) => {\n if (reverse) {\n return 1 - (1 - t) ** 4;\n }\n return t ** 4;\n};\n\nconst quarticPosition: PositionFunction = (t: number, reverse = false) => {\n if (reverse) {\n return 1 - (1 - t) ** 5;\n }\n return t ** 5;\n};\n\nconst sinusoidalPosition: PositionFunction = (t: number, reverse = false) => {\n if (reverse) {\n return 1 - Math.sin(((1 - t) * Math.PI) / 2);\n }\n return Math.sin((t * Math.PI) / 2);\n};\n\nconst asinusoidalPosition: PositionFunction = (t: number, reverse = false) => {\n if (reverse) {\n return 1 - Math.asin(1 - t) / (Math.PI / 2);\n }\n return Math.asin(t) / (Math.PI / 2);\n};\n\nconst arcPosition: PositionFunction = (t: number, reverse = false) => {\n if (reverse) {\n return Math.sqrt(1 - (1 - t) ** 2);\n }\n return 1 - Math.sqrt(1 - t);\n};\n\nconst smoothStepPosition: PositionFunction = (t: number) => {\n return t ** 2 * (3 - 2 * t);\n};\n\nexport const positionFunctions = {\n linearPosition,\n exponentialPosition,\n quadraticPosition,\n cubicPosition,\n quarticPosition,\n sinusoidalPosition,\n asinusoidalPosition,\n arcPosition,\n smoothStepPosition,\n};\n\n/**\n * Calculates the distance between two points\n * @param p1 The first point\n * @param p2 The second point\n * @param hueMode Whether to use the hue distance function\n * @returns The distance between the two points\n * @example\n * const p1 = [0, 0, 0];\n * const p2 = [1, 1, 1];\n * const dist = distance(p1, p2);\n * console.log(dist); // 1.7320508075688772\n **/\nconst distance = (\n p1: PartialVector3,\n p2: PartialVector3,\n hueMode = false\n): number => {\n const a1 = p1[0];\n const a2 = p2[0];\n let diffA = 0;\n\n if (hueMode && a1 !== null && a2 !== null) {\n diffA = Math.min(Math.abs(a1 - a2), 360 - Math.abs(a1 - a2));\n diffA = diffA / 360;\n } else {\n diffA = a1 === null || a2 === null ? 0 : a1 - a2;\n }\n\n const a = diffA;\n const b = p1[1] === null || p2[1] === null ? 0 : p2[1] - p1[1];\n const c = p1[2] === null || p2[2] === null ? 0 : p2[2] - p1[2];\n\n return Math.sqrt(a * a + b * b + c * c);\n};\n\nexport type ColorPointCollection = {\n xyz?: Vector3;\n color?: Vector3;\n invertedLightness?: boolean;\n};\n\nclass ColorPoint {\n public x = 0;\n public y = 0;\n public z = 0;\n public color: Vector3 = [0, 0, 0];\n private _invertedLightness = false;\n\n constructor({ xyz, color, invertedLightness }: ColorPointCollection = {}) {\n this._invertedLightness = invertedLightness || false;\n this.positionOrColor({ xyz, color, invertedLightness });\n }\n\n positionOrColor({ xyz, color, invertedLightness }: ColorPointCollection) {\n if (xyz && color) {\n throw new Error(\"Point must be initialized with either x,y,z or hsl\");\n } else if (xyz) {\n this.x = xyz[0];\n this.y = xyz[1];\n this.z = xyz[2];\n this.color = pointToHSL(\n [this.x, this.y, this.z],\n invertedLightness || false\n );\n } else if (color) {\n this.color = color;\n [this.x, this.y, this.z] = hslToPoint(color, invertedLightness || false);\n }\n }\n\n set position([x, y, z]: Vector3) {\n this.x = x;\n this.y = y;\n this.z = z;\n this.color = pointToHSL(\n [this.x, this.y, this.z] as Vector3,\n this._invertedLightness\n );\n }\n\n get position(): Vector3 {\n return [this.x, this.y, this.z];\n }\n\n set hsl([h, s, l]: Vector3) {\n this.color = [h, s, l];\n [this.x, this.y, this.z] = hslToPoint(\n this.color as Vector3,\n this._invertedLightness\n );\n }\n\n get hsl(): Vector3 {\n return this.color;\n }\n\n get hslCSS(): string {\n const [h, s, l] = this.color;\n return `hsl(${h.toFixed(2)}, ${(s * 100).toFixed(2)}%, ${(l * 100).toFixed(2)}%)`;\n }\n\n get oklchCSS(): string {\n const [h, s, l] = this.color;\n return `oklch(${(l * 100).toFixed(2)}% ${(s * .4).toFixed(3)} ${h.toFixed(2)})`;\n }\n\n get lchCSS(): string {\n const [h, s, l] = this.color;\n return `lch(${(l * 100).toFixed(2)}% ${(s * 150).toFixed(2)} ${h.toFixed(2)})`;\n }\n\n shiftHue(angle: number): void {\n this.color[0] = (360 + (this.color[0] + angle)) % 360;\n [this.x, this.y, this.z] = hslToPoint(\n this.color as Vector3,\n this._invertedLightness\n );\n }\n}\n\nexport type PolineOptions = {\n anchorColors: Vector3[];\n numPoints: number;\n positionFunction?: (t: number, invert?: boolean) => number;\n positionFunctionX?: (t: number, invert?: boolean) => number;\n positionFunctionY?: (t: number, invert?: boolean) => number;\n positionFunctionZ?: (t: number, invert?: boolean) => number;\n invertedLightness?: boolean;\n closedLoop?: boolean;\n};\nexport class Poline {\n private _needsUpdate = true;\n private _anchorPoints: ColorPoint[];\n\n private _numPoints: number;\n private points: ColorPoint[][];\n\n private _positionFunctionX = sinusoidalPosition;\n private _positionFunctionY = sinusoidalPosition;\n private _positionFunctionZ = sinusoidalPosition;\n\n private _anchorPairs: ColorPoint[][];\n\n private connectLastAndFirstAnchor = false;\n\n private _animationFrame: null | number = null;\n\n private _invertedLightness = false;\n\n constructor(\n {\n anchorColors = randomHSLPair(),\n numPoints = 4,\n positionFunction = sinusoidalPosition,\n positionFunctionX,\n positionFunctionY,\n positionFunctionZ,\n closedLoop,\n invertedLightness,\n }: PolineOptions = {\n anchorColors: randomHSLPair(),\n numPoints: 4,\n positionFunction: sinusoidalPosition,\n closedLoop: false,\n }\n ) {\n if (!anchorColors || anchorColors.length < 2) {\n throw new Error(\"Must have at least two anchor colors\");\n }\n\n this._anchorPoints = anchorColors.map(\n (point) => new ColorPoint({ color: point, invertedLightness })\n );\n\n this._numPoints = numPoints + 2; // add two for the anchor points\n\n this._positionFunctionX =\n positionFunctionX || positionFunction || sinusoidalPosition;\n this._positionFunctionY =\n positionFunctionY || positionFunction || sinusoidalPosition;\n this._positionFunctionZ =\n positionFunctionZ || positionFunction || sinusoidalPosition;\n\n this.connectLastAndFirstAnchor = closedLoop || false;\n\n this._invertedLightness = invertedLightness || false;\n\n this.updateAnchorPairs();\n }\n\n public get numPoints(): number {\n return this._numPoints - 2;\n }\n\n public set numPoints(numPoints: number) {\n if (numPoints < 1) {\n throw new Error(\"Must have at least one point\");\n }\n this._numPoints = numPoints + 2; // add two for the anchor points\n this.updateAnchorPairs();\n }\n\n public set positionFunction(\n positionFunction: PositionFunction | PositionFunction[]\n ) {\n if (Array.isArray(positionFunction)) {\n if (positionFunction.length !== 3) {\n throw new Error(\"Position function array must have 3 elements\");\n }\n if (\n typeof positionFunction[0] !== \"function\" ||\n typeof positionFunction[1] !== \"function\" ||\n typeof positionFunction[2] !== \"function\"\n ) {\n throw new Error(\"Position function array must have 3 functions\");\n }\n this._positionFunctionX = positionFunction[0];\n this._positionFunctionY = positionFunction[1];\n this._positionFunctionZ = positionFunction[2];\n } else {\n this._positionFunctionX = positionFunction;\n this._positionFunctionY = positionFunction;\n this._positionFunctionZ = positionFunction;\n }\n\n this.updateAnchorPairs();\n }\n\n public get positionFunction(): PositionFunction | PositionFunction[] {\n // not to sure what to do here, because the position function is a combination of the three\n if (\n this._positionFunctionX === this._positionFunctionY &&\n this._positionFunctionX === this._positionFunctionZ\n ) {\n return this._positionFunctionX;\n }\n\n return [\n this._positionFunctionX,\n this._positionFunctionY,\n this._positionFunctionZ,\n ];\n }\n\n public set positionFunctionX(positionFunctionX: PositionFunction) {\n this._positionFunctionX = positionFunctionX;\n this.updateAnchorPairs();\n }\n\n public get positionFunctionX(): PositionFunction {\n return this._positionFunctionX;\n }\n\n public set positionFunctionY(positionFunctionY: PositionFunction) {\n this._positionFunctionY = positionFunctionY;\n this.updateAnchorPairs();\n }\n\n public get positionFunctionY(): PositionFunction {\n return this._positionFunctionY;\n }\n\n public set positionFunctionZ(positionFunctionZ: PositionFunction) {\n this._positionFunctionZ = positionFunctionZ;\n this.updateAnchorPairs();\n }\n\n public get positionFunctionZ(): PositionFunction {\n return this._positionFunctionZ;\n }\n\n public get anchorPoints(): ColorPoint[] {\n return this._anchorPoints;\n }\n\n public set anchorPoints(anchorPoints: ColorPoint[]) {\n this._anchorPoints = anchorPoints;\n this.updateAnchorPairs();\n }\n\n public updateAnchorPairs(): void {\n this._anchorPairs = [] as ColorPoint[][];\n\n const anchorPointsLength = this.connectLastAndFirstAnchor\n ? this.anchorPoints.length\n : this.anchorPoints.length - 1;\n\n for (let i = 0; i < anchorPointsLength; i++) {\n const pair = [\n this.anchorPoints[i],\n this.anchorPoints[(i + 1) % this.anchorPoints.length],\n ] as ColorPoint[];\n\n this._anchorPairs.push(pair);\n }\n\n this.points = this._anchorPairs.map((pair, i) => {\n const p1position = pair[0] ? pair[0].position : ([0, 0, 0] as Vector3);\n const p2position = pair[1] ? pair[1].position : ([0, 0, 0] as Vector3);\n\n return vectorsOnLine(\n p1position,\n p2position,\n this._numPoints,\n i % 2 ? true : false,\n this.positionFunctionX,\n this.positionFunctionY,\n this.positionFunctionZ\n ).map(\n (p) =>\n new ColorPoint({ xyz: p, invertedLightness: this._invertedLightness })\n );\n });\n }\n\n public addAnchorPoint({\n xyz,\n color,\n insertAtIndex,\n }: ColorPointCollection & { insertAtIndex: number }): ColorPoint {\n const newAnchor = new ColorPoint({\n xyz,\n color,\n invertedLightness: this._invertedLightness,\n });\n if (insertAtIndex) {\n this.anchorPoints.splice(insertAtIndex, 0, newAnchor);\n } else {\n this.anchorPoints.push(newAnchor);\n }\n this.updateAnchorPairs();\n return newAnchor;\n }\n\n public removeAnchorPoint({\n point,\n index,\n }: {\n point?: ColorPoint;\n index?: number;\n }): void {\n if (!point && index === undefined) {\n throw new Error(\"Must provide a point or index\");\n }\n\n let apid;\n\n if (index !== undefined) {\n apid = index;\n } else if (point) {\n apid = this.anchorPoints.indexOf(point);\n }\n\n if (apid > -1 && apid < this.anchorPoints.length) {\n this.anchorPoints.splice(apid, 1);\n this.updateAnchorPairs();\n } else {\n throw new Error(\"Point not found\");\n }\n }\n\n public updateAnchorPoint({\n point,\n pointIndex,\n xyz,\n color,\n }: {\n point?: ColorPoint;\n pointIndex?: number;\n } & ColorPointCollection): ColorPoint {\n if (pointIndex) {\n point = this.anchorPoints[pointIndex];\n }\n\n if (!point) {\n throw new Error(\"Must provide a point or pointIndex\");\n }\n\n if (!xyz && !color) {\n throw new Error(\"Must provide a new xyz position or color\");\n }\n\n if (xyz) point.position = xyz;\n if (color) point.hsl = color;\n\n this.updateAnchorPairs();\n\n return point;\n }\n\n public getClosestAnchorPoint({\n xyz,\n hsl,\n maxDistance = 1,\n }: {\n xyz?: PartialVector3;\n hsl?: PartialVector3;\n maxDistance?: number;\n }): ColorPoint | null {\n if (!xyz && !hsl) {\n throw new Error(\"Must provide a xyz or hsl\");\n }\n\n let distances;\n\n if (xyz) {\n distances = this.anchorPoints.map((anchor) =>\n distance(anchor.position, xyz)\n );\n } else if (hsl) {\n distances = this.anchorPoints.map((anchor) =>\n distance(anchor.hsl, hsl, true)\n );\n }\n\n const minDistance = Math.min(...distances);\n\n if (minDistance > maxDistance) {\n return null;\n }\n\n const closestAnchorIndex = distances.indexOf(minDistance);\n\n return this.anchorPoints[closestAnchorIndex] || null;\n }\n\n public set closedLoop(newStatus: boolean) {\n this.connectLastAndFirstAnchor = newStatus;\n this.updateAnchorPairs();\n }\n\n public get closedLoop(): boolean {\n return this.connectLastAndFirstAnchor;\n }\n\n public set invertedLightness(newStatus: boolean) {\n this._invertedLightness = newStatus;\n this.updateAnchorPairs();\n }\n\n public get invertedLightness(): boolean {\n return this._invertedLightness;\n }\n\n public get flattenedPoints() {\n return this.points\n .flat()\n .filter((p, i) => (i != 0 ? i % this._numPoints : true));\n }\n\n public get colors() {\n const colors = this.flattenedPoints.map((p) => p.color);\n if (this.connectLastAndFirstAnchor) {\n colors.pop();\n }\n return colors;\n }\n\n public cssColors(mode: \"hsl\" | \"oklch\" | \"lch\" = \"hsl\") {\n const methods = {\n hsl: (p) => p.hslCSS,\n oklch: (p) => p.oklchCSS,\n lch: (p) => p.lchCSS,\n };\n const cssColors = this.flattenedPoints.map(methods[mode]);\n if (this.connectLastAndFirstAnchor) {\n cssColors.pop();\n }\n return cssColors;\n }\n\n public get colorsCSS() {\n return this.cssColors('hsl');\n }\n\n public get colorsCSSlch() {\n return this.cssColors('lch');\n }\n\n public get colorsCSSoklch() {\n return this.cssColors('oklch');\n }\n\n public shiftHue(hShift = 20): void {\n this.anchorPoints.forEach((p) => p.shiftHue(hShift));\n this.updateAnchorPairs();\n }\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nconst { p5 } = globalThis as any;\n\nif (p5) {\n console.info(\"p5 detected, adding poline to p5 prototype\");\n\n const poline = new Poline();\n p5.prototype.poline = poline;\n\n const polineColors = () =>\n poline.colors.map(\n (c) => `hsl(${Math.round(c[0])},${c[1] * 100}%,${c[2] * 100}%)`\n );\n p5.prototype.polineColors = polineColors;\n p5.prototype.registerMethod(\"polineColors\", p5.prototype.polineColors);\n\n globalThis.poline = poline;\n globalThis.polineColors = polineColors;\n}\n"], | ||
| "mappings": "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", | ||
| "sourcesContent": ["/* eslint-disable @typescript-eslint/ban-ts-comment */\nexport type FuncNumberReturn = (arg0: number) => Vector2;\nexport type Vector2 = [number, number];\nexport type Vector3 = [number, ...Vector2];\nexport type PartialVector3 = [number | null, number | null, number | null];\n\ntype CSSColorMethods = {\n hsl: (p: ColorPoint) => string;\n oklch: (p: ColorPoint) => string;\n lch: (p: ColorPoint) => string;\n};\n\n/**\n * Converts the given (x, y, z) coordinate to an HSL color\n * The (x, y) values are used to calculate the hue, while the z value is used as the saturation\n * The lightness value is calculated based on the distance of (x, y) from the center (0.5, 0.5)\n * Returns an array [hue, saturation, lightness]\n * @param xyz:Vector3 [x, y, z] coordinate array in (x, y, z) format (0-1, 0-1, 0-1)\n * @returns [hue, saturation, lightness]: Vector3 color array in HSL format (0-360, 0-1, 0-1)\n * @example\n * pointToHSL([0.5, 0.5, 1]) // [0, 1, 0.5]\n * pointToHSL([0.5, 0.5, 0]) // [0, 1, 0]\n **/\n\nexport const pointToHSL = (\n xyz: Vector3,\n invertedLightness: boolean\n): Vector3 => {\n const [x, y, z] = xyz;\n\n // cy and cx are the center (y and x) values\n const cx = 0.5;\n const cy = 0.5;\n\n // Calculate the angle between the point (x, y) and the center (cx, cy)\n const radians = Math.atan2(y - cy, x - cx);\n\n // Convert the angle to degrees and shift it so that it goes from 0 to 360\n let deg = radians * (180 / Math.PI);\n deg = (360 + deg) % 360;\n\n // The saturation value is taken from the z coordinate\n const s = z;\n\n // Calculate the lightness value based on the distance from the center\n const dist = Math.sqrt(Math.pow(y - cy, 2) + Math.pow(x - cx, 2));\n const l = dist / cx;\n\n // Return the HSL color as an array [hue, saturation, lightness]\n return [deg, s, invertedLightness ? 1 - l : l];\n};\n\n/**\n * Converts the given HSL color to an (x, y, z) coordinate\n * The hue value is used to calculate the (x, y) position, while the saturation value is used as the z coordinate\n * The lightness value is used to calculate the distance from the center (0.5, 0.5)\n * Returns an array [x, y, z]\n * @param hsl:Vector3 [hue, saturation, lightness] color array in HSL format (0-360, 0-1, 0-1)\n * @returns [x, y, z]:Vector3 coordinate array in (x, y, z) format (0-1, 0-1, 0-1)\n * @example\n * hslToPoint([0, 1, 0.5]) // [0.5, 0.5, 1]\n * hslToPoint([0, 1, 0]) // [0.5, 0.5, 1]\n * hslToPoint([0, 1, 1]) // [0.5, 0.5, 1]\n * hslToPoint([0, 0, 0.5]) // [0.5, 0.5, 0]\n **/\nexport const hslToPoint = (\n hsl: Vector3,\n invertedLightness: boolean\n): Vector3 => {\n // Destructure the input array into separate hue, saturation, and lightness values\n const [h, s, l] = hsl;\n // cx and cy are the center (x and y) values\n const cx = 0.5;\n const cy = 0.5;\n // Calculate the angle in radians based on the hue value\n const radians = h / (180 / Math.PI);\n\n // Calculate the distance from the center based on the lightness value\n const dist = (invertedLightness ? 1 - l : l) * cx;\n\n // Calculate the x and y coordinates based on the distance and angle\n const x = cx + dist * Math.cos(radians);\n const y = cy + dist * Math.sin(radians);\n // The z coordinate is equal to the saturation value\n const z = s;\n // Return the (x, y, z) coordinate as an array [x, y, z]\n return [x, y, z];\n};\n\nexport const randomHSLPair = (\n startHue: number = Math.random() * 360,\n saturations: Vector2 = [Math.random(), Math.random()],\n lightnesses: Vector2 = [0.75 + Math.random() * 0.2, 0.3 + Math.random() * 0.2]\n): [Vector3, Vector3] => [\n [startHue, saturations[0], lightnesses[0]],\n [(startHue + 60 + Math.random() * 180) % 360, saturations[1], lightnesses[1]],\n];\n\nexport const randomHSLTriple = (\n startHue: number = Math.random() * 360,\n saturations: Vector3 = [Math.random(), Math.random(), Math.random()],\n lightnesses: Vector3 = [\n 0.75 + Math.random() * 0.2,\n Math.random() * 0.2,\n 0.75 + Math.random() * 0.2,\n ]\n): [Vector3, Vector3, Vector3] => [\n [startHue, saturations[0], lightnesses[0]],\n [(startHue + 60 + Math.random() * 180) % 360, saturations[1], lightnesses[1]],\n [(startHue + 60 + Math.random() * 180) % 360, saturations[2], lightnesses[2]],\n];\n\nconst vectorOnLine = (\n t: number,\n p1: Vector3,\n p2: Vector3,\n invert = false,\n fx = (t: number, invert: boolean): number => (invert ? 1 - t : t),\n fy = (t: number, invert: boolean): number => (invert ? 1 - t : t),\n fz = (t: number, invert: boolean): number => (invert ? 1 - t : t)\n): Vector3 => {\n const tModifiedX = fx(t, invert);\n const tModifiedY = fy(t, invert);\n const tModifiedZ = fz(t, invert);\n const x = (1 - tModifiedX) * p1[0] + tModifiedX * p2[0];\n const y = (1 - tModifiedY) * p1[1] + tModifiedY * p2[1];\n const z = (1 - tModifiedZ) * p1[2] + tModifiedZ * p2[2];\n\n return [x, y, z];\n};\n\nconst vectorsOnLine = (\n p1: Vector3,\n p2: Vector3,\n numPoints = 4,\n invert = false,\n fx = (t: number, invert: boolean): number => (invert ? 1 - t : t),\n fy = (t: number, invert: boolean): number => (invert ? 1 - t : t),\n fz = (t: number, invert: boolean): number => (invert ? 1 - t : t)\n): Vector3[] => {\n const points: Vector3[] = [];\n\n for (let i = 0; i < numPoints; i++) {\n const [x, y, z] = vectorOnLine(\n i / (numPoints - 1),\n p1,\n p2,\n invert,\n fx,\n fy,\n fz\n );\n points.push([x, y, z]);\n }\n\n return points;\n};\n\nexport type PositionFunction = (t: number, reverse?: boolean) => number;\n\nconst linearPosition: PositionFunction = (t: number) => {\n return t;\n};\n\nconst exponentialPosition: PositionFunction = (t: number, reverse = false) => {\n if (reverse) {\n return 1 - (1 - t) ** 2;\n }\n return t ** 2;\n};\n\nconst quadraticPosition: PositionFunction = (t: number, reverse = false) => {\n if (reverse) {\n return 1 - (1 - t) ** 3;\n }\n return t ** 3;\n};\n\nconst cubicPosition: PositionFunction = (t: number, reverse = false) => {\n if (reverse) {\n return 1 - (1 - t) ** 4;\n }\n return t ** 4;\n};\n\nconst quarticPosition: PositionFunction = (t: number, reverse = false) => {\n if (reverse) {\n return 1 - (1 - t) ** 5;\n }\n return t ** 5;\n};\n\nconst sinusoidalPosition: PositionFunction = (t: number, reverse = false) => {\n if (reverse) {\n return 1 - Math.sin(((1 - t) * Math.PI) / 2);\n }\n return Math.sin((t * Math.PI) / 2);\n};\n\nconst asinusoidalPosition: PositionFunction = (t: number, reverse = false) => {\n if (reverse) {\n return 1 - Math.asin(1 - t) / (Math.PI / 2);\n }\n return Math.asin(t) / (Math.PI / 2);\n};\n\nconst arcPosition: PositionFunction = (t: number, reverse = false) => {\n if (reverse) {\n return 1 - Math.sqrt(1 - t ** 2);\n }\n return 1 - Math.sqrt(1 - t);\n};\n\nconst smoothStepPosition: PositionFunction = (t: number) => {\n return t ** 2 * (3 - 2 * t);\n};\n\nexport const positionFunctions = {\n linearPosition,\n exponentialPosition,\n quadraticPosition,\n cubicPosition,\n quarticPosition,\n sinusoidalPosition,\n asinusoidalPosition,\n arcPosition,\n smoothStepPosition,\n};\n\n/**\n * Calculates the distance between two points\n * @param p1 The first point\n * @param p2 The second point\n * @param hueMode Whether to use the hue distance function\n * @returns The distance between the two points\n * @example\n * const p1 = [0, 0, 0];\n * const p2 = [1, 1, 1];\n * const dist = distance(p1, p2);\n * console.log(dist); // 1.7320508075688772\n **/\nconst distance = (\n p1: PartialVector3,\n p2: PartialVector3,\n hueMode = false\n): number => {\n const a1 = p1[0];\n const a2 = p2[0];\n let diffA = 0;\n\n if (hueMode && a1 !== null && a2 !== null) {\n diffA = Math.min(Math.abs(a1 - a2), 360 - Math.abs(a1 - a2));\n diffA = diffA / 360;\n } else {\n diffA = a1 === null || a2 === null ? 0 : a1 - a2;\n }\n\n const a = diffA;\n const b = p1[1] === null || p2[1] === null ? 0 : p2[1] - p1[1];\n const c = p1[2] === null || p2[2] === null ? 0 : p2[2] - p1[2];\n\n return Math.sqrt(a * a + b * b + c * c);\n};\n\nexport type ColorPointCollection = {\n xyz?: Vector3;\n color?: Vector3;\n invertedLightness?: boolean;\n};\n\nexport class ColorPoint {\n public x = 0;\n public y = 0;\n public z = 0;\n public color: Vector3 = [0, 0, 0];\n private _invertedLightness = false;\n\n constructor({\n xyz,\n color,\n invertedLightness = false,\n }: ColorPointCollection = {}) {\n this._invertedLightness = invertedLightness;\n this.positionOrColor({ xyz, color, invertedLightness });\n }\n\n positionOrColor({\n xyz,\n color,\n invertedLightness = false,\n }: ColorPointCollection) {\n if ((xyz && color) || (!xyz && !color)) {\n throw new Error(\"Point must be initialized with either x,y,z or hsl\");\n } else if (xyz) {\n this.x = xyz[0];\n this.y = xyz[1];\n this.z = xyz[2];\n this.color = pointToHSL([this.x, this.y, this.z], invertedLightness);\n } else if (color) {\n this.color = color;\n [this.x, this.y, this.z] = hslToPoint(color, invertedLightness);\n }\n }\n\n set position([x, y, z]: Vector3) {\n this.x = x;\n this.y = y;\n this.z = z;\n this.color = pointToHSL(\n [this.x, this.y, this.z] as Vector3,\n this._invertedLightness\n );\n }\n\n get position(): Vector3 {\n return [this.x, this.y, this.z];\n }\n\n set hsl([h, s, l]: Vector3) {\n this.color = [h, s, l];\n [this.x, this.y, this.z] = hslToPoint(\n this.color as Vector3,\n this._invertedLightness\n );\n }\n\n get hsl(): Vector3 {\n return this.color;\n }\n\n get hslCSS(): string {\n const [h, s, l] = this.color;\n return `hsl(${h.toFixed(2)}, ${(s * 100).toFixed(2)}%, ${(l * 100).toFixed(\n 2\n )}%)`;\n }\n\n get oklchCSS(): string {\n const [h, s, l] = this.color;\n return `oklch(${(l * 100).toFixed(2)}% ${(s * 0.4).toFixed(3)} ${h.toFixed(\n 2\n )})`;\n }\n\n get lchCSS(): string {\n const [h, s, l] = this.color;\n return `lch(${(l * 100).toFixed(2)}% ${(s * 150).toFixed(2)} ${h.toFixed(\n 2\n )})`;\n }\n\n shiftHue(angle: number): void {\n this.color[0] = (360 + (this.color[0] + angle)) % 360;\n [this.x, this.y, this.z] = hslToPoint(\n this.color as Vector3,\n this._invertedLightness\n );\n }\n}\n\nexport type PolineOptions = {\n anchorColors: Vector3[];\n numPoints: number;\n positionFunction?: (t: number, invert?: boolean) => number;\n positionFunctionX?: (t: number, invert?: boolean) => number;\n positionFunctionY?: (t: number, invert?: boolean) => number;\n positionFunctionZ?: (t: number, invert?: boolean) => number;\n invertedLightness?: boolean;\n closedLoop?: boolean;\n};\nexport class Poline {\n private _needsUpdate = true;\n private _anchorPoints: ColorPoint[];\n\n private _numPoints: number;\n private points: ColorPoint[][];\n\n private _positionFunctionX = sinusoidalPosition;\n private _positionFunctionY = sinusoidalPosition;\n private _positionFunctionZ = sinusoidalPosition;\n\n private _anchorPairs: ColorPoint[][];\n\n private connectLastAndFirstAnchor = false;\n\n private _animationFrame: null | number = null;\n\n private _invertedLightness = false;\n\n constructor(\n {\n anchorColors = randomHSLPair(),\n numPoints = 4,\n positionFunction = sinusoidalPosition,\n positionFunctionX,\n positionFunctionY,\n positionFunctionZ,\n closedLoop,\n invertedLightness,\n }: PolineOptions = {\n anchorColors: randomHSLPair(),\n numPoints: 4,\n positionFunction: sinusoidalPosition,\n closedLoop: false,\n }\n ) {\n if (!anchorColors || anchorColors.length < 2) {\n throw new Error(\"Must have at least two anchor colors\");\n }\n\n this._anchorPoints = anchorColors.map(\n (point) => new ColorPoint({ color: point, invertedLightness })\n );\n\n this._numPoints = numPoints + 2; // add two for the anchor points\n\n this._positionFunctionX =\n positionFunctionX || positionFunction || sinusoidalPosition;\n this._positionFunctionY =\n positionFunctionY || positionFunction || sinusoidalPosition;\n this._positionFunctionZ =\n positionFunctionZ || positionFunction || sinusoidalPosition;\n\n this.connectLastAndFirstAnchor = closedLoop || false;\n\n this._invertedLightness = invertedLightness || false;\n\n this.updateAnchorPairs();\n }\n\n public get numPoints(): number {\n return this._numPoints - 2;\n }\n\n public set numPoints(numPoints: number) {\n if (numPoints < 1) {\n throw new Error(\"Must have at least one point\");\n }\n this._numPoints = numPoints + 2; // add two for the anchor points\n this.updateAnchorPairs();\n }\n\n public set positionFunction(\n positionFunction: PositionFunction | PositionFunction[]\n ) {\n if (Array.isArray(positionFunction)) {\n if (positionFunction.length !== 3) {\n throw new Error(\"Position function array must have 3 elements\");\n }\n if (\n typeof positionFunction[0] !== \"function\" ||\n typeof positionFunction[1] !== \"function\" ||\n typeof positionFunction[2] !== \"function\"\n ) {\n throw new Error(\"Position function array must have 3 functions\");\n }\n this._positionFunctionX = positionFunction[0];\n this._positionFunctionY = positionFunction[1];\n this._positionFunctionZ = positionFunction[2];\n } else {\n this._positionFunctionX = positionFunction;\n this._positionFunctionY = positionFunction;\n this._positionFunctionZ = positionFunction;\n }\n\n this.updateAnchorPairs();\n }\n\n public get positionFunction(): PositionFunction | PositionFunction[] {\n // not to sure what to do here, because the position function is a combination of the three\n if (\n this._positionFunctionX === this._positionFunctionY &&\n this._positionFunctionX === this._positionFunctionZ\n ) {\n return this._positionFunctionX;\n }\n\n return [\n this._positionFunctionX,\n this._positionFunctionY,\n this._positionFunctionZ,\n ];\n }\n\n public set positionFunctionX(positionFunctionX: PositionFunction) {\n this._positionFunctionX = positionFunctionX;\n this.updateAnchorPairs();\n }\n\n public get positionFunctionX(): PositionFunction {\n return this._positionFunctionX;\n }\n\n public set positionFunctionY(positionFunctionY: PositionFunction) {\n this._positionFunctionY = positionFunctionY;\n this.updateAnchorPairs();\n }\n\n public get positionFunctionY(): PositionFunction {\n return this._positionFunctionY;\n }\n\n public set positionFunctionZ(positionFunctionZ: PositionFunction) {\n this._positionFunctionZ = positionFunctionZ;\n this.updateAnchorPairs();\n }\n\n public get positionFunctionZ(): PositionFunction {\n return this._positionFunctionZ;\n }\n\n public get anchorPoints(): ColorPoint[] {\n return this._anchorPoints;\n }\n\n public set anchorPoints(anchorPoints: ColorPoint[]) {\n this._anchorPoints = anchorPoints;\n this.updateAnchorPairs();\n }\n\n public updateAnchorPairs(): void {\n this._anchorPairs = [] as ColorPoint[][];\n\n const anchorPointsLength = this.connectLastAndFirstAnchor\n ? this.anchorPoints.length\n : this.anchorPoints.length - 1;\n\n for (let i = 0; i < anchorPointsLength; i++) {\n const pair = [\n this.anchorPoints[i],\n this.anchorPoints[(i + 1) % this.anchorPoints.length],\n ] as ColorPoint[];\n\n this._anchorPairs.push(pair);\n }\n\n this.points = this._anchorPairs.map((pair, i) => {\n const p1position = pair[0] ? pair[0].position : ([0, 0, 0] as Vector3);\n const p2position = pair[1] ? pair[1].position : ([0, 0, 0] as Vector3);\n\n return vectorsOnLine(\n p1position,\n p2position,\n this._numPoints,\n i % 2 ? true : false,\n this.positionFunctionX,\n this.positionFunctionY,\n this.positionFunctionZ\n ).map(\n (p) =>\n new ColorPoint({ xyz: p, invertedLightness: this._invertedLightness })\n );\n });\n }\n\n public addAnchorPoint({\n xyz,\n color,\n insertAtIndex,\n }: ColorPointCollection & { insertAtIndex?: number }): ColorPoint {\n const newAnchor = new ColorPoint({\n xyz,\n color,\n invertedLightness: this._invertedLightness,\n });\n if (insertAtIndex !== undefined) {\n this.anchorPoints.splice(insertAtIndex, 0, newAnchor);\n } else {\n this.anchorPoints.push(newAnchor);\n }\n this.updateAnchorPairs();\n return newAnchor;\n }\n\n public removeAnchorPoint({\n point,\n index,\n }: {\n point?: ColorPoint;\n index?: number;\n }): void {\n if (!point && index === undefined) {\n throw new Error(\"Must provide a point or index\");\n }\n\n if (this.anchorPoints.length < 3) {\n throw new Error(\"Must have at least two anchor points\");\n }\n\n let apid;\n\n if (index !== undefined) {\n apid = index;\n } else if (point) {\n apid = this.anchorPoints.indexOf(point);\n }\n\n if (apid > -1 && apid < this.anchorPoints.length) {\n this.anchorPoints.splice(apid, 1);\n this.updateAnchorPairs();\n } else {\n throw new Error(\"Point not found\");\n }\n }\n\n public updateAnchorPoint({\n point,\n pointIndex,\n xyz,\n color,\n }: {\n point?: ColorPoint;\n pointIndex?: number;\n } & ColorPointCollection): ColorPoint {\n if (pointIndex !== undefined) {\n point = this.anchorPoints[pointIndex];\n }\n\n if (!point) {\n throw new Error(\"Must provide a point or pointIndex\");\n }\n\n if (!xyz && !color) {\n throw new Error(\"Must provide a new xyz position or color\");\n }\n\n if (xyz) point.position = xyz;\n if (color) point.hsl = color;\n\n this.updateAnchorPairs();\n\n return point;\n }\n\n public getClosestAnchorPoint({\n xyz,\n hsl,\n maxDistance = 1,\n }: {\n xyz?: PartialVector3;\n hsl?: PartialVector3;\n maxDistance?: number;\n }): ColorPoint | null {\n if (!xyz && !hsl) {\n throw new Error(\"Must provide a xyz or hsl\");\n }\n\n let distances;\n\n if (xyz) {\n distances = this.anchorPoints.map((anchor) =>\n distance(anchor.position, xyz)\n );\n } else if (hsl) {\n distances = this.anchorPoints.map((anchor) =>\n distance(anchor.hsl, hsl, true)\n );\n }\n\n const minDistance = Math.min(...distances);\n\n if (minDistance > maxDistance) {\n return null;\n }\n\n const closestAnchorIndex = distances.indexOf(minDistance);\n\n return this.anchorPoints[closestAnchorIndex] || null;\n }\n\n public set closedLoop(newStatus: boolean) {\n this.connectLastAndFirstAnchor = newStatus;\n this.updateAnchorPairs();\n }\n\n public get closedLoop(): boolean {\n return this.connectLastAndFirstAnchor;\n }\n\n public set invertedLightness(newStatus: boolean) {\n this._invertedLightness = newStatus;\n this.updateAnchorPairs();\n }\n\n public get invertedLightness(): boolean {\n return this._invertedLightness;\n }\n\n /**\n * Returns a flattened array of all points across all segments,\n * removing duplicated anchor points at segment boundaries.\n *\n * Since anchor points exist at both the end of one segment and\n * the beginning of the next, this method keeps only one instance of each.\n * The filter logic keeps the first point (index 0) and then filters out\n * points whose indices are multiples of the segment size (_numPoints),\n * which are the anchor points at the start of each segment (except the first).\n *\n * This approach ensures we get all unique points in the correct order\n * while avoiding duplicated anchor points.\n *\n * @returns {ColorPoint[]} A flat array of unique ColorPoint instances\n */\n public get flattenedPoints() {\n return this.points\n .flat()\n .filter((p, i) => (i != 0 ? i % this._numPoints : true));\n }\n\n public get colors() {\n const colors = this.flattenedPoints.map((p) => p.color);\n if (this.connectLastAndFirstAnchor) {\n colors.pop();\n }\n return colors;\n }\n\n public cssColors(mode: \"hsl\" | \"oklch\" | \"lch\" = \"hsl\"): string[] {\n const methods: CSSColorMethods = {\n hsl: (p: ColorPoint): string => p.hslCSS,\n oklch: (p: ColorPoint): string => p.oklchCSS,\n lch: (p: ColorPoint): string => p.lchCSS,\n };\n const cssColors = this.flattenedPoints.map(methods[mode]);\n if (this.connectLastAndFirstAnchor) {\n cssColors.pop();\n }\n return cssColors;\n }\n\n public get colorsCSS() {\n return this.cssColors(\"hsl\");\n }\n\n public get colorsCSSlch() {\n return this.cssColors(\"lch\");\n }\n\n public get colorsCSSoklch() {\n return this.cssColors(\"oklch\");\n }\n\n public shiftHue(hShift = 20): void {\n this.anchorPoints.forEach((p) => p.shiftHue(hShift));\n this.updateAnchorPairs();\n }\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nconst { p5 } = globalThis as any;\n\nif (p5) {\n console.info(\"p5 detected, adding poline to p5 prototype\");\n\n const poline = new Poline();\n p5.prototype.poline = poline;\n\n const polineColors = () =>\n poline.colors.map(\n (c) => `hsl(${Math.round(c[0])},${c[1] * 100}%,${c[2] * 100}%)`\n );\n p5.prototype.polineColors = polineColors;\n p5.prototype.registerMethod(\"polineColors\", p5.prototype.polineColors);\n\n globalThis.poline = poline;\n globalThis.polineColors = polineColors;\n}\n"], | ||
| "mappings": "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", | ||
| "names": ["t", "invert"] | ||
| } |
+36
-12
@@ -35,2 +35,3 @@ (function(root, factory) { | ||
| __export(src_exports, { | ||
| ColorPoint: () => ColorPoint, | ||
| Poline: () => Poline, | ||
@@ -145,3 +146,3 @@ hslToPoint: () => hslToPoint, | ||
| if (reverse) { | ||
| return Math.sqrt(1 - __pow(1 - t, 2)); | ||
| return 1 - Math.sqrt(1 - __pow(t, 2)); | ||
| } | ||
@@ -180,3 +181,7 @@ return 1 - Math.sqrt(1 - t); | ||
| var ColorPoint = class { | ||
| constructor({ xyz, color, invertedLightness } = {}) { | ||
| constructor({ | ||
| xyz, | ||
| color, | ||
| invertedLightness = false | ||
| } = {}) { | ||
| this.x = 0; | ||
@@ -187,7 +192,11 @@ this.y = 0; | ||
| this._invertedLightness = false; | ||
| this._invertedLightness = invertedLightness || false; | ||
| this._invertedLightness = invertedLightness; | ||
| this.positionOrColor({ xyz, color, invertedLightness }); | ||
| } | ||
| positionOrColor({ xyz, color, invertedLightness }) { | ||
| if (xyz && color) { | ||
| positionOrColor({ | ||
| xyz, | ||
| color, | ||
| invertedLightness = false | ||
| }) { | ||
| if (xyz && color || !xyz && !color) { | ||
| throw new Error("Point must be initialized with either x,y,z or hsl"); | ||
@@ -198,9 +207,6 @@ } else if (xyz) { | ||
| this.z = xyz[2]; | ||
| this.color = pointToHSL( | ||
| [this.x, this.y, this.z], | ||
| invertedLightness || false | ||
| ); | ||
| this.color = pointToHSL([this.x, this.y, this.z], invertedLightness); | ||
| } else if (color) { | ||
| this.color = color; | ||
| [this.x, this.y, this.z] = hslToPoint(color, invertedLightness || false); | ||
| [this.x, this.y, this.z] = hslToPoint(color, invertedLightness); | ||
| } | ||
@@ -395,3 +401,3 @@ } | ||
| }); | ||
| if (insertAtIndex) { | ||
| if (insertAtIndex !== void 0) { | ||
| this.anchorPoints.splice(insertAtIndex, 0, newAnchor); | ||
@@ -411,2 +417,5 @@ } else { | ||
| } | ||
| if (this.anchorPoints.length < 3) { | ||
| throw new Error("Must have at least two anchor points"); | ||
| } | ||
| let apid; | ||
@@ -431,3 +440,3 @@ if (index !== void 0) { | ||
| }) { | ||
| if (pointIndex) { | ||
| if (pointIndex !== void 0) { | ||
| point = this.anchorPoints[pointIndex]; | ||
@@ -487,2 +496,17 @@ } | ||
| } | ||
| /** | ||
| * Returns a flattened array of all points across all segments, | ||
| * removing duplicated anchor points at segment boundaries. | ||
| * | ||
| * Since anchor points exist at both the end of one segment and | ||
| * the beginning of the next, this method keeps only one instance of each. | ||
| * The filter logic keeps the first point (index 0) and then filters out | ||
| * points whose indices are multiples of the segment size (_numPoints), | ||
| * which are the anchor points at the start of each segment (except the first). | ||
| * | ||
| * This approach ensures we get all unique points in the correct order | ||
| * while avoiding duplicated anchor points. | ||
| * | ||
| * @returns {ColorPoint[]} A flat array of unique ColorPoint instances | ||
| */ | ||
| get flattenedPoints() { | ||
@@ -489,0 +513,0 @@ return this.points.flat().filter((p, i) => i != 0 ? i % this._numPoints : true); |
+11
-3
| { | ||
| "name": "poline", | ||
| "version": "0.7.0", | ||
| "version": "0.8.0", | ||
| "description": "color palette generator mico-lib", | ||
@@ -11,4 +11,12 @@ "type": "module", | ||
| "exports": { | ||
| "require": "./dist/index.cjs", | ||
| "import": "./dist/index.mjs" | ||
| ".": { | ||
| "require": "./dist/index.cjs", | ||
| "import": "./dist/index.mjs", | ||
| "types": "./dist/index.d.ts" | ||
| }, | ||
| "./picker": { | ||
| "require": "./dist/picker.cjs", | ||
| "import": "./dist/picker.mjs", | ||
| "types": "./dist/webcomponent.d.ts" | ||
| } | ||
| }, | ||
@@ -15,0 +23,0 @@ "types": "dist/index.d.ts", |
+426
-24
| <h1><a href="https://meodai.github.io/poline/"><img width="200" src="https://meodai.github.io/poline/poline-logo.png" alt="poline" /></a></h1> | ||
| ## Esoteric Color palette Generator Mico-Lib | ||
| ## Esoteric Color Palette Generator Micro-Lib | ||
@@ -9,4 +9,41 @@ "**poline**" is an enigmatic color palette generator, that harnesses the mystical witchcraft of polar coordinates. Its methodology, defying conventional color science, is steeped in the esoteric knowledge of the early 20th century. This magical technology defies explanation, drawing lines between anchors to produce visually striking and otherworldly palettes. It is an indispensable tool for the modern generative sorcerer, and a delight for the eye. | ||
| ## Installation | ||
| "**poline**" is available as an [npm package](https://www.npmjs.com/package/poline). Alternatively you can clone it on [GitHub](https://github.com/meodai/poline). | ||
| ```bash | ||
| npm install poline | ||
| ``` | ||
| You can also use the [unpkg CDN](https://unpkg.com/poline) to include the library in your project. | ||
| I recommend using the **mjs** version of the library. This will allow you to use the **import** syntax. But you can also use the **umd** version if you prefer to use the **script** tag. | ||
| ```html | ||
| <script type="module"> | ||
| import { | ||
| Poline | ||
| } from 'https://unpkg.com/poline?module' | ||
| </script> | ||
| ``` | ||
| ## Getting Started | ||
| Begin your journey with **poline** by following this simple incantation: | ||
| ```js | ||
| // Import the magical construct | ||
| import { Poline } from 'poline'; | ||
| // Summon a new palette with default settings (random anchor colors) | ||
| const poline = new Poline(); | ||
| // Behold the colors in HSL format | ||
| console.log(poline.colors); | ||
| // Or as CSS strings ready for your spells | ||
| console.log(poline.colorsCSS); | ||
| ``` | ||
| ## Summoning | ||
| The use of "**Poline**" begins with the invocation of its command, which can be performed with or without arguments. If called without, the tool will generate a mesmerizing palette featuring two randomly selected **anchors.** | ||
@@ -26,2 +63,3 @@ On the other hand, one can choose to provide their own **anchor** points, represented as a list of **hsl** values, for a more personal touch. The power to shape and mold the colors lies in your hands." | ||
| ## Points | ||
| The magic of "**Poline**" is revealed through its technique of drawing lines between anchor points. The richness of the palette is determined by the number of **points**, with each connection producing a unique color. | ||
@@ -43,3 +81,3 @@ | ||
| The choice is yours, whether to provide your own anchor points during initialization or to allow "**Poline**" to generate a random selection for you by omitting the 'anchorColors' argument. The versatility of Poline extends "**Poline**" its initial setup, as you can also add anchors to your palette at any time using the '**addAnchor**' method. This method accepts either a **color** as HSL array values or an array of **X, Y, Z** coordinates, further expanding the possibilities of your color creation. | ||
| The choice is yours, whether to provide your own anchor points during initialization or to allow "**Poline**" to generate a random selection for you by omitting the 'anchorColors' argument. The versatility of Poline extends "**Poline**" its initial setup, as you can also add anchors to your palette at any time using the '**addAnchorPoint**' method. This method accepts either a **color** as HSL array values or an array of **X, Y, Z** coordinates, further expanding the possibilities of your color creation. | ||
@@ -58,2 +96,11 @@ ```js | ||
| You can also specify where to insert the new anchor by providing an `insertAtIndex` parameter: | ||
| ```js | ||
| poline.addAnchorPoint({ | ||
| color: [200, 0.5, 0.6], | ||
| insertAtIndex: 1 // Insert after the first anchor | ||
| }); | ||
| ``` | ||
| ## Updating Anchors | ||
@@ -72,2 +119,11 @@ | ||
| You can also update an anchor by its index: | ||
| ```js | ||
| poline.updateAnchorPoint({ | ||
| pointIndex: 1, | ||
| color: [120, 0.8, 0.5] | ||
| }); | ||
| ``` | ||
| ## Position Function | ||
@@ -101,4 +157,15 @@ | ||
| - arcPosition | ||
| - smoothStepPosition | ||
| Here's a visual representation of how these functions affect the distribution: | ||
| | Function Name | Effect on Color Distribution | | ||
| |---------------|------------------------------| | ||
| | linearPosition | Even distribution of colors along the path | | ||
| | exponentialPosition | Colors cluster near one end, spreading out toward the other | | ||
| | sinusoidalPosition | Smooth acceleration and deceleration of colors | | ||
| | arcPosition | Colors follow an arc-like distribution | | ||
| ## Arcs | ||
| By defining **different position functions for each axis**, you can control the distribution of colors along each axis (**positionFunctionX**, **positionFunctionY**, **positionFunctionZ**). This will draw different arcs and create a diverse range of color palettes. | ||
@@ -119,3 +186,2 @@ | ||
| By default, the palette is not a closed loop. This means that the last color generated is not the same as the first color. If you want the palette to be a closed loop, you can set the **closedLoop** argument to true. | ||
@@ -125,3 +191,2 @@ | ||
| poline.closedLoop = true; | ||
| ``` | ||
@@ -157,6 +222,10 @@ | ||
| The '**poline**' instance returns all colors as an array of **hsl** arrays or alternatively as an array of **CSS** strings. | ||
| The '**poline**' instance returns all colors as an array of **hsl**, **lch** or | ||
| **oklch** arrays or alternatively as an array of **CSS** strings. | ||
| ```js | ||
| poline.colors | ||
| poline.colorsCSS | ||
| poline.colors // Array of HSL values [[h, s, l], [h, s, l], ...] | ||
| poline.colorsCSS // Array of CSS HSL strings ['hsl(h, s%, l%)', ...] | ||
| poline.colorsCSSlch // Array of CSS LCH strings ['lch(l% c h)', ...] | ||
| poline.colorsCSSoklch // Array of CSS OKLCH strings ['oklch(l% c h)', ...] | ||
| ``` | ||
@@ -167,2 +236,3 @@ | ||
| To remove an anchor, you can use the **removeAnchorPoint** method. It either takes an **anchor** reference or an **index** as an argument. | ||
| ```js | ||
@@ -180,2 +250,18 @@ poline.removeAnchorPoint({ | ||
| ## Inverted Lightness | ||
| The magical construct of "**poline**" offers the power to invert the lightness calculation, creating palettes with different visual characteristics. You can toggle this option during initialization or later through the instance property. | ||
| ```js | ||
| // During initialization | ||
| const poline = new Poline({ | ||
| invertedLightness: true | ||
| }); | ||
| // Or later | ||
| poline.invertedLightness = true; | ||
| ``` | ||
| When inverted, colors near the center of the coordinate system will have higher lightness values, while colors at the edge will be darker, creating a different aesthetic in your palette. | ||
| ## Color Model | ||
@@ -193,6 +279,5 @@ | ||
| mode: 'okhsl', | ||
| { | ||
| h: c.hsl[0], | ||
| s: c.hsl[1], | ||
| l: c.hsl[2]} | ||
| h: c[0], | ||
| s: c[1], | ||
| l: c[2] | ||
| }) | ||
@@ -203,7 +288,5 @@ ); | ||
| mode: 'lch', | ||
| { | ||
| h: hsl[0], | ||
| c: hsl[1] * 51.484, | ||
| l: hsl[2] * 100, | ||
| } | ||
| h: c[0], | ||
| c: c[1] * 51.484, | ||
| l: c[2] * 100, | ||
| }) | ||
@@ -213,18 +296,337 @@ ); | ||
| ## Installation | ||
| ## Common Use Cases | ||
| ### Creating a Gradient | ||
| "**poline**" can be used to generate CSS gradients with unique color distributions: | ||
| ```js | ||
| const poline = new Poline({ | ||
| anchorColors: [ | ||
| [210, 0.8, 0.6], // Blue | ||
| [30, 0.8, 0.6] // Orange | ||
| ], | ||
| numPoints: 8 | ||
| }); | ||
| // Generate a CSS linear gradient | ||
| const colors = poline.colorsCSS; | ||
| const gradient = `linear-gradient(in oklab, ${colors.join(', ')})`; | ||
| // Apply to an element | ||
| document.getElementById('gradient').style.background = gradient; | ||
| ``` | ||
| ### Generating Color Schemes for Data Visualization | ||
| "**poline**" excels at creating color schemes for data visualization. In this | ||
| case, this makes a great diverging color scheme for a chart: | ||
| ```js | ||
| // Create a palette with perceptually distinct colors | ||
| const poline = new Poline({ | ||
| anchorColors: [ | ||
| [10, 0.70, 0.90], | ||
| [70, 0.97, 0], | ||
| [260, 0.70, 0.0] | ||
| ], | ||
| positionFunction: positionFunctions.linearPosition, | ||
| numPoints: 7, | ||
| closedLoop: true | ||
| }); | ||
| // Use the colors for chart elements | ||
| const chartColors = poline.colorsCSS; | ||
| ``` | ||
| ### Animating Palettes | ||
| You can animate your "**poline**" palette to create mesmerizing effects: | ||
| ```js | ||
| const poline = new Poline(); | ||
| let animationFrame; | ||
| function animatePalette() { | ||
| // Shift the hue slightly each frame | ||
| poline.shiftHue(0.5); | ||
| // Update elements with new colors | ||
| const elements = document.querySelectorAll('.color-element'); | ||
| const colors = poline.colorsCSS; | ||
| elements.forEach((el, i) => { | ||
| el.style.backgroundColor = colors[i % colors.length]; | ||
| }); | ||
| animationFrame = requestAnimationFrame(animatePalette); | ||
| } | ||
| // Start/stop animation | ||
| document.getElementById('toggle-animation').addEventListener('click', () => { | ||
| if (animationFrame) { | ||
| cancelAnimationFrame(animationFrame); | ||
| animationFrame = null; | ||
| } else { | ||
| animatePalette(); | ||
| } | ||
| }); | ||
| ``` | ||
| ## Error Handling | ||
| "**poline**" will conjure mystical errors when improper incantations are attempted. Be prepared to handle these manifestations: | ||
| - When providing fewer than two anchor colors: `"Must have at least two anchor colors"` | ||
| - When setting `numPoints` to less than 1: `"Must have at least one point"` | ||
| - When removing too many anchors: `"Must have at least two anchor points"` | ||
| - When providing invalid parameters: `"Point must be initialized with either x,y,z or hsl"` | ||
| - When the anchor point is not found: `"Point not found"` | ||
| Example of proper error handling: | ||
| ```js | ||
| try { | ||
| const poline = new Poline({ | ||
| anchorColors: [[100, 0.5, 0.5]] // Only one anchor color! | ||
| }); | ||
| } catch (error) { | ||
| console.error('Failed to summon palette:', error.message); | ||
| // Fallback to default settings | ||
| const poline = new Poline(); | ||
| } | ||
| ``` | ||
| ## TypeScript Support | ||
| "**poline**" is written in TypeScript and provides type definitions for all its features. The main types you'll encounter: | ||
| ```typescript | ||
| // Basic vector types | ||
| type Vector2 = [number, number]; | ||
| type Vector3 = [number, ...Vector2]; | ||
| type PartialVector3 = [number | null, number | null, number | null]; | ||
| // Position function type | ||
| type PositionFunction = (t: number, reverse?: boolean) => number; | ||
| // Options for creating a Poline instance | ||
| type PolineOptions = { | ||
| anchorColors: Vector3[]; | ||
| numPoints: number; | ||
| positionFunction?: PositionFunction; | ||
| positionFunctionX?: PositionFunction; | ||
| positionFunctionY?: PositionFunction; | ||
| positionFunctionZ?: PositionFunction; | ||
| invertedLightness?: boolean; | ||
| closedLoop?: boolean; | ||
| }; | ||
| // Color point collection | ||
| type ColorPointCollection = { | ||
| xyz?: Vector3; | ||
| color?: Vector3; | ||
| invertedLightness?: boolean; | ||
| }; | ||
| ``` | ||
| ## API Reference | ||
| Behold the arcane interface of "**poline**", detailed in full for your enlightenment: | ||
| ### Poline Class | ||
| #### Constructor (ColorPoint Class) | ||
| ```typescript | ||
| constructor(options?: PolineOptions) | ||
| ``` | ||
| #### Properties of the Poline Class | ||
| - `numPoints: number` - Get/set the number of points between anchors | ||
| - `positionFunction: PositionFunction | PositionFunction[]` - Get/set the position function(s) | ||
| - `positionFunctionX: PositionFunction` - Get/set the X-axis position function | ||
| - `positionFunctionY: PositionFunction` - Get/set the Y-axis position function | ||
| - `positionFunctionZ: PositionFunction` - Get/set the Z-axis position function | ||
| - `anchorPoints: ColorPoint[]` - Get/set the anchor points | ||
| - `closedLoop: boolean` - Get/set whether the palette forms a closed loop | ||
| - `invertedLightness: boolean` - Get/set whether lightness calculation is inverted | ||
| - `flattenedPoints: ColorPoint[]` - Get all points in a flat array | ||
| - `colors: Vector3[]` - Get all colors as HSL arrays | ||
| - `colorsCSS: string[]` - Get all colors as CSS HSL strings | ||
| - `colorsCSSlch: string[]` - Get all colors as CSS LCH strings | ||
| - `colorsCSSoklch: string[]` - Get all colors as CSS OKLCH strings | ||
| #### Methods of the ColorPoint Class | ||
| - `updateAnchorPairs(): void` - Update internal anchor pairs | ||
| - `addAnchorPoint(options: ColorPointCollection & { insertAtIndex?: number }): ColorPoint` - Add a new anchor point | ||
| - `removeAnchorPoint(options: { point?: ColorPoint; index?: number }): void` - Remove an anchor point | ||
| - `updateAnchorPoint(options: { point?: ColorPoint; pointIndex?: number } & ColorPointCollection): ColorPoint` - Update an anchor point | ||
| - `getClosestAnchorPoint(options: { xyz?: PartialVector3; hsl?: PartialVector3; maxDistance?: number }): ColorPoint | null` - Find closest anchor point | ||
| - `shiftHue(hShift?: number): void` - Shift the hue of all colors | ||
| ### ColorPoint Class | ||
| #### Constructor | ||
| ```typescript | ||
| constructor(options?: ColorPointCollection) | ||
| ``` | ||
| #### Properties | ||
| - `position: Vector3` - Get/set the XYZ position | ||
| - `hsl: Vector3` - Get/set the HSL color | ||
| - `hslCSS: string` - Get the CSS HSL string | ||
| - `oklchCSS: string` - Get the CSS OKLCH string | ||
| - `lchCSS: string` - Get the CSS LCH string | ||
| #### Methods | ||
| - `positionOrColor(options: ColorPointCollection): void` - Set position or color | ||
| - `shiftHue(angle: number): void` - Shift the hue of the color | ||
| ### Position Functions | ||
| All position functions have the signature: | ||
| ```typescript | ||
| (t: number, reverse?: boolean) => number | ||
| ``` | ||
| Available functions: | ||
| - `linearPosition` | ||
| - `exponentialPosition` | ||
| - `quadraticPosition` | ||
| - `cubicPosition` | ||
| - `quarticPosition` | ||
| - `sinusoidalPosition` | ||
| - `asinusoidalPosition` | ||
| - `arcPosition` | ||
| - `smoothStepPosition` | ||
| ## Web Component | ||
| "**poline**" also provides a web component called `<poline-picker>` that creates an interactive color wheel for visualizing and manipulating your color palettes. This interface allows users to drag anchor points and see real-time updates to their palette. | ||
| ### Basic Usage and Setup | ||
| ```html | ||
| <script type="module"> | ||
| import { Poline, PolinePicker } from 'https://unpkg.com/poline/dist/picker.mjs'; | ||
| </script> | ||
| <poline-picker id="picker" interactive allow-add-points></poline-picker> | ||
| <script> | ||
| const picker = document.getElementById('picker'); | ||
| // Create and set a palette | ||
| const poline = new Poline({ | ||
| anchorColors: [[300, 0.8, 0.7], [60, 0.9, 0.5]], | ||
| numPoints: 6 | ||
| }); | ||
| picker.setPoline(poline); | ||
| // Listen for changes | ||
| picker.addEventListener('poline-change', (event) => { | ||
| console.log('New colors:', event.detail.poline.colorsCSS); | ||
| }); | ||
| </script> | ||
| ``` | ||
| ### Attributes | ||
| The `<poline-picker>` component supports the following attributes: | ||
| - `interactive` - Enables dragging of anchor points | ||
| - `allow-add-points` - Allows adding new anchor points by clicking on empty areas | ||
| ### Styling the Component | ||
| The picker can be styled using CSS custom properties: | ||
| ```css | ||
| poline-picker { | ||
| width: 300px; | ||
| height: 300px; | ||
| /* Customize the appearance */ | ||
| --poline-picker-line-color: #333; | ||
| --poline-picker-bg-color: #fff; | ||
| } | ||
| ``` | ||
| ### API Methods | ||
| The `<poline-picker>` element provides several methods for programmatic control: | ||
| ```javascript | ||
| const picker = document.querySelector('poline-picker'); | ||
| // Set a new palette | ||
| picker.setPoline(myPoline); | ||
| // Enable/disable adding points | ||
| picker.setAllowAddPoints(true); | ||
| // Add a point at specific coordinates (x, y in pixels) | ||
| const newPoint = picker.addPointAtPosition(150, 100); | ||
| ``` | ||
| ### Events | ||
| The component dispatches a `poline-change` event whenever the palette is modified: | ||
| ```javascript | ||
| picker.addEventListener('poline-change', (event) => { | ||
| const { poline } = event.detail; | ||
| // Access the updated palette | ||
| console.log('Updated palette:', poline.colorsCSS); | ||
| }); | ||
| ``` | ||
| ## Web Component Installation | ||
| "**poline**" is available as an [npm package](https://www.npmjs.com/package/poline). Alternatively you can clone it on [GitHub](https://github.com/meodai/poline). | ||
| ```js | ||
| ```bash | ||
| npm install poline | ||
| ``` | ||
| You can also use the [unpkg CDN](https://unpkg.com/poline) to include the library in your project. | ||
| I recommend using the **mjs** version of the library. This will allow you to use the **import** syntax. But you can also use the **umd** version if you prefer to use the **script** tag. | ||
| ```html | ||
| import { | ||
| Poline | ||
| } from 'https://unpkg.com/poline?module' | ||
| <script type="module"> | ||
| import { | ||
| Poline, | ||
| PolinePicker | ||
| } from 'https://unpkg.com/poline/dist/picker.mjs' | ||
| </script> | ||
| ``` | ||
| ```html | ||
| <poline-picker id="picker" interactive allow-add-points></poline-picker> | ||
| ``` | ||
| ### Using the Web Component | ||
| For the interactive `<poline-picker>` web component, use the picker build: | ||
| ```html | ||
| <script type="module"> | ||
| import { | ||
| Poline, | ||
| PolinePicker | ||
| } from 'https://unpkg.com/poline/dist/picker.mjs' | ||
| </script> | ||
| ``` | ||
| Or install via npm and import: | ||
| ```javascript | ||
| import { Poline, PolinePicker } from 'poline/dist/picker.mjs'; | ||
| ``` | ||
| ## License | ||
@@ -234,4 +636,4 @@ | ||
| The project is [MIT licensed](https://github.com/meodai/poline/blob/main/LICENSE) and open source. If you find any bugs or have any suggestions please open an issue on [GitHub](). | ||
| The project is [MIT licensed](https://github.com/meodai/poline/blob/main/LICENSE) and open source. If you find any bugs or have any suggestions please open an issue on [GitHub](https://github.com/meodai/poline/issues). | ||
| Inspired and created with the blessing of [Anatoly Zenkov](https://anatolyzenkov.com/) |
+51
-22
@@ -7,2 +7,8 @@ /* eslint-disable @typescript-eslint/ban-ts-comment */ | ||
| type CSSColorMethods = { | ||
| hsl: (p: ColorPoint) => string; | ||
| oklch: (p: ColorPoint) => string; | ||
| lch: (p: ColorPoint) => string; | ||
| }; | ||
| /** | ||
@@ -16,5 +22,4 @@ * Converts the given (x, y, z) coordinate to an HSL color | ||
| * @example | ||
| * pointToHSL(0.5, 0.5, 1) // [0, 1, 0.5] | ||
| * pointToHSL(0.5, 0.5, 0) // [0, 1, 0] | ||
| * pointToHSL(0.5, 0.5, 1) // [0, 1, 1] | ||
| * pointToHSL([0.5, 0.5, 1]) // [0, 1, 0.5] | ||
| * pointToHSL([0.5, 0.5, 0]) // [0, 1, 0] | ||
| **/ | ||
@@ -206,3 +211,3 @@ | ||
| if (reverse) { | ||
| return Math.sqrt(1 - (1 - t) ** 2); | ||
| return 1 - Math.sqrt(1 - t ** 2); | ||
| } | ||
@@ -269,3 +274,3 @@ return 1 - Math.sqrt(1 - t); | ||
| class ColorPoint { | ||
| export class ColorPoint { | ||
| public x = 0; | ||
@@ -277,9 +282,17 @@ public y = 0; | ||
| constructor({ xyz, color, invertedLightness }: ColorPointCollection = {}) { | ||
| this._invertedLightness = invertedLightness || false; | ||
| constructor({ | ||
| xyz, | ||
| color, | ||
| invertedLightness = false, | ||
| }: ColorPointCollection = {}) { | ||
| this._invertedLightness = invertedLightness; | ||
| this.positionOrColor({ xyz, color, invertedLightness }); | ||
| } | ||
| positionOrColor({ xyz, color, invertedLightness }: ColorPointCollection) { | ||
| if (xyz && color) { | ||
| positionOrColor({ | ||
| xyz, | ||
| color, | ||
| invertedLightness = false, | ||
| }: ColorPointCollection) { | ||
| if ((xyz && color) || (!xyz && !color)) { | ||
| throw new Error("Point must be initialized with either x,y,z or hsl"); | ||
@@ -290,9 +303,6 @@ } else if (xyz) { | ||
| this.z = xyz[2]; | ||
| this.color = pointToHSL( | ||
| [this.x, this.y, this.z], | ||
| invertedLightness || false | ||
| ); | ||
| this.color = pointToHSL([this.x, this.y, this.z], invertedLightness); | ||
| } else if (color) { | ||
| this.color = color; | ||
| [this.x, this.y, this.z] = hslToPoint(color, invertedLightness || false); | ||
| [this.x, this.y, this.z] = hslToPoint(color, invertedLightness); | ||
| } | ||
@@ -556,3 +566,3 @@ } | ||
| insertAtIndex, | ||
| }: ColorPointCollection & { insertAtIndex: number }): ColorPoint { | ||
| }: ColorPointCollection & { insertAtIndex?: number }): ColorPoint { | ||
| const newAnchor = new ColorPoint({ | ||
@@ -563,3 +573,3 @@ xyz, | ||
| }); | ||
| if (insertAtIndex) { | ||
| if (insertAtIndex !== undefined) { | ||
| this.anchorPoints.splice(insertAtIndex, 0, newAnchor); | ||
@@ -584,2 +594,6 @@ } else { | ||
| if (this.anchorPoints.length < 3) { | ||
| throw new Error("Must have at least two anchor points"); | ||
| } | ||
| let apid; | ||
@@ -610,3 +624,3 @@ | ||
| } & ColorPointCollection): ColorPoint { | ||
| if (pointIndex) { | ||
| if (pointIndex !== undefined) { | ||
| point = this.anchorPoints[pointIndex]; | ||
@@ -685,2 +699,17 @@ } | ||
| /** | ||
| * Returns a flattened array of all points across all segments, | ||
| * removing duplicated anchor points at segment boundaries. | ||
| * | ||
| * Since anchor points exist at both the end of one segment and | ||
| * the beginning of the next, this method keeps only one instance of each. | ||
| * The filter logic keeps the first point (index 0) and then filters out | ||
| * points whose indices are multiples of the segment size (_numPoints), | ||
| * which are the anchor points at the start of each segment (except the first). | ||
| * | ||
| * This approach ensures we get all unique points in the correct order | ||
| * while avoiding duplicated anchor points. | ||
| * | ||
| * @returns {ColorPoint[]} A flat array of unique ColorPoint instances | ||
| */ | ||
| public get flattenedPoints() { | ||
@@ -700,7 +729,7 @@ return this.points | ||
| public cssColors(mode: "hsl" | "oklch" | "lch" = "hsl") { | ||
| const methods = { | ||
| hsl: (p) => p.hslCSS, | ||
| oklch: (p) => p.oklchCSS, | ||
| lch: (p) => p.lchCSS, | ||
| public cssColors(mode: "hsl" | "oklch" | "lch" = "hsl"): string[] { | ||
| const methods: CSSColorMethods = { | ||
| hsl: (p: ColorPoint): string => p.hslCSS, | ||
| oklch: (p: ColorPoint): string => p.oklchCSS, | ||
| lch: (p: ColorPoint): string => p.lchCSS, | ||
| }; | ||
@@ -707,0 +736,0 @@ const cssColors = this.flattenedPoints.map(methods[mode]); |
Sorry, the diff of this file is too big to display
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
1365815
13.04%34
41.67%6212
106.38%625
180.27%8
60%8
Infinity%