├── .gitignore ├── README.md ├── palettesnap ├── PaletteSnap.py ├── __init__.py ├── background.py ├── cache.py ├── cli.py ├── colorClass.py ├── colorOptimize.py ├── console.py ├── outdatedCheck.py ├── preview.py ├── refresh.py ├── setup.py ├── templating.py └── wallpaper.py ├── poetry.lock └── pyproject.toml /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | __pycache__/ 3 | dist/ 4 | tests/ 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 |
3 |

PaletteSnap

4 |

5 | Generate readable color palettes from any image! 6 |

7 |

8 | Static Badge 9 | PyPI - Python Version 10 | PyPI - Version 11 | Pepy Total Downlods 12 |

13 |
14 | 15 | **The README file gives you a "snap" of PaletteSnap. Consult the [Wiki](https://github.com/EmperorEntropy/PaletteSnap/wiki) for more detailed information.** 16 | 17 | ## Installation 18 | PaletteSnap is a python program, so Python is needed. It can be installed via pip: 19 | ``` 20 | pip install palettesnap 21 | ``` 22 | It is not recommended to install PaletteSnap via the Github repository due to changes that may or may not break. Wait for new releases, and download them via `pip`. 23 | 24 | Note that PaletteSnap depends on many dependencies, and it might take a while for it to execute during its first run. 25 | 26 | ## Demonstration 27 | 28 | https://github.com/user-attachments/assets/5afe9fac-1e76-48cf-af2e-3f04894dfc3a 29 | 30 | ## Examples 31 | ![](https://snipboard.io/06j3rB.jpg) 32 | 33 | ![](https://snipboard.io/w6vlx3.jpg) 34 | 35 | ![](https://snipboard.io/tBTeh6.jpg) 36 | 37 | ## Features 38 | - Generates a color palette from any image. 39 | - Color palette are guaranteed to be readable with good contrast. 40 | - Options to increase color variety for palette. Useful for monochromatic images. 41 | - Assigned color roles. No more guessing which color does want with the palette. 42 | - Sets wallpaper to your image automatically. 43 | - Uses templates so that you can apply the color palette to your entire system. 44 | - Has caching so you can load pre-generated palettes fast. 45 | - Supports program refreshing. No more closing and reopening programs to get them to update their palette. 46 | - Auto checks for updates. Get notified when a new version gets released. 47 | 48 | ### Comparison 49 | Here is a table that compares PaletteSnap with pywal: 50 | | | **pywal** | **PaletteSnap** | 51 | |:-----------------------------:|:------------------:|:------------------:| 52 | | **Palette Generation** | :white_check_mark: | :white_check_mark: | 53 | | **Wallpaper Setting** | :white_check_mark: | :white_check_mark: | 54 | | **Templating** | :white_check_mark: | :white_check_mark: | 55 | | **Readable Palette** | :warning: | :white_check_mark: | 56 | | **Preview Palette** | :white_check_mark: | :white_check_mark: | 57 | | **Assigned Color Roles** | :x: | :white_check_mark: | 58 | | **Color Variety Options** | :x: | :white_check_mark: | 59 | | **Refreshes Programs** | :x: | :white_check_mark: | 60 | | **Custom Background Color** | :white_check_mark: | :warning: | 61 | | **Cached colorschemes** | :white_check_mark: | :white_check_mark: | 62 | | **Color palette modes** | :warning: | :white_check_mark: | 63 | | **Random palette** | :white_check_mark: | :white_check_mark: | 64 | | **Update Checker** | :x: | :white_check_mark: | 65 | 66 | ### Wallpaper Setting 67 | PaletteSnap's functionality is supported by all OS that has Python. However, the wallpaper switching functionality may not be supported for certain OS. Here is a list of all OS that PaletteSnap's wallpaper switching supports: 68 | - macOS (tested) 69 | - GNOME Linux (untested) 70 | - KDE Plasma Linux (tested) 71 | - XFCE Linux (untested) 72 | - MATE Linux (untested) 73 | 74 | If you have one of the untested OS, and it works, please let me know. If you have an OS that is not on the list, feel free to suggest it! 75 | 76 | ## Usage 77 | When generating the palette, PaletteSnap creates two folders. They are 78 | - `XDG_CONFIG_HOME/palsnap` 79 | - `XDG_CACHE_HOME/palsnap` 80 | 81 | On a macOS, they are `~/.config/palsnap/` and `~/.cache/palsnap/`. The folders palsnap can be found in are the same folders pywal's config and cache are found in. 82 | 83 | *For the sake of convenience, the locations of these folders will be referred to as macOs's in the rest of the README file.* 84 | 85 | ### Palette Generation 86 | To generate a palette, run the following command: 87 | `palsnap gen ` 88 | This is the simplest way to generate a palette. If you run `palsnap gen -h`, you'll notice there is a list of options. 89 | 90 | If you keep hitting the *manual optimization* for your generated palette, this is usually a sign that your current options are not good. If you haven't already, an easy way to avoid it is to set your `mode` to either light or dark instead of auto. 91 | 92 | **See [Palette Generation](https://github.com/EmperorEntropy/PaletteSnap/wiki/Palette-Generation) for further information.** 93 | 94 | ### Preview 95 | In order to preview a palette, you must first generate it. You can skip all the other steps in the process by running `palsnap gen --skip`. After that, run `palsnap preview` to preview the palette. The output will be the image (only for certain terminals) and all the colors. Your terminal window must be big enough to see all the colors. 96 | 97 | If you want a more direct approach, in the `~/.cache/palsnap` folder, there should be a `PaletteTest.html`. You can use it to see what your palette will actually look like if you use it. 98 | 99 | **See [Previewing](https://github.com/EmperorEntropy/PaletteSnap/wiki/Previewing) for further information.** 100 | 101 | ### Templating 102 | All templating information is done in `~/.config/palsnap/templates.toml` and templates are stored in the templates folder found at `~/.config/palsnap/templates/`. 103 | 104 | **See [Templating](https://github.com/EmperorEntropy/PaletteSnap/wiki/Templating) for further information.** 105 | 106 | ### Caching 107 | To save time, it is possible to cache palettes. You can either cache palettes as you generate them with the `--cache` option, or you can cache the palette right after generating it with: 108 | ``` 109 | palsnap cache set 110 | ``` 111 | The latter method is useful if you want to generate and preview the palette before caching it. There are many other cache commands like `load`, `clear`, `rename`, etc. 112 | 113 | **See [Caching](https://github.com/EmperorEntropy/PaletteSnap/wiki/Caching) for further information.** 114 | 115 | ### Color Palette 116 | Based on Everforest's palette usage, 117 | | Identifier | Usages | 118 | |---|---| 119 | | `bg` | Default Background | 120 | | `bg1` | Unused | 121 | | `bg2` | Unused | 122 | | `bg3` | Comment color | 123 | | `bg4` | Selection background color | 124 | | `bg5` | Unused | 125 | | `foreground` | Default Foreground, [_Treesitter_: Constants, Variables, Function Parameters, Properties, Symbol Identifiers] | 126 | | `red` | Conditional Keywords, Loop Keywords, Exception Keywords, Inclusion Keywords, Uncategorised Keywords, Diff Deleted Signs, Error Messages, Error Signs | 127 | | `orange` | Operator Keywords, Operators, Labels, Storage Classes, Composite Types, Enumerated Types, Tags, Title, Debugging Statements | 128 | | `yellow` | Types, Special Characters, Warning Messages, Warning Signs, [_Treesitter_: Modules, Namespaces] | 129 | | `green` | Function Names, Method Names, Strings, Characters, Hint Messages, Hint Signs, Search Highlights, [_Treesitter_: Constructors, Function Calls, Built-In Functions, Macro Functions, String Escapes, Regex Literals, Tag Delimiters, Non-Structured Text] | 130 | | `cyan` | Constants, Macros, [_Treesitter_: Strings, Characters] | 131 | | `blue` | Identifiers, Uncategorised Special Symbols, Diff Changed Text Background, Info Messages, Info Signs, [_Treesitter_: Fields, Special Punctuation, Math Environments] | 132 | | `magenta` | Booleans, Numbers, Preprocessors, [_Treesitter_: Built-In Constants, Built-In Variables, Macro-Defined Constants, Attributes/Annotations] | 133 | | `violet` | Unused. Can be used to replace `magenta` if wanted. | 134 | | `white` | ANSI white | 135 | | `black` | ANSI black | 136 | 137 | `cusor_bg` is usually just the foreground. `cursor_fg` is usually just the background. 138 | 139 | **See [Color Roles](https://github.com/EmperorEntropy/PaletteSnap/wiki/Color-Roles) for further information.** 140 | 141 | ## To Do 142 | - Add option for users to define number of bg gradients. 143 | - Add more flexibility to determine bg. 144 | - Add check for templating that returns list of variables not replaced. 145 | - Add wallpaper setting support for more Linux distributions. 146 | - Add image preview support for more terminals. 147 | 148 | ## Acknowledgments 149 | - [pywal](https://github.com/dylanaraps/pywal) for the inspiration that led me to create PaletteSnap. 150 | - Contributors in pull requests for adding or fixing features. 151 | - [ozwaldorf](https://github.com/ozwaldorf/) for helpful suggestions and advice during PaletteSnap's initial development. Check out [lutgen](https://github.com/ozwaldorf/lutgen-rs), a program that themes any image to a desktop colorscheme. 152 | - [Everforest colorscheme](https://github.com/sainnhe/everforest/) for its colorscheme usage. 153 | - [Solarized](https://github.com/altercation/solarized) and [Selenized](https://github.com/jan-warchol/selenized) colorschemes for giving me valuable insights on what a palette should be like. 154 | -------------------------------------------------------------------------------- /palettesnap/PaletteSnap.py: -------------------------------------------------------------------------------- 1 | ### 2 | # Modules 3 | ### 4 | 5 | # Internal Modules 6 | from . import setup 7 | from .colorOptimize import performOptimal 8 | from .console import console 9 | from .colorClass import Color, rgbColor, hexColor, lchColor 10 | from .background import findBgGradient 11 | 12 | # External Modules 13 | import numpy as np 14 | import numpy.typing as npt 15 | #import sklearn 16 | from pykdtree.kdtree import KDTree 17 | from kmedoids import fasterpam 18 | from sklearn.cluster import KMeans 19 | from sklearn.metrics import pairwise_distances 20 | from colour import read_image, sRGB_to_XYZ, XYZ_to_Oklab 21 | import concurrent.futures 22 | import mixbox 23 | import os 24 | import toml 25 | 26 | # Use python3 -m pip 27 | # We use the Oklab colorspace 28 | 29 | ### 30 | # Config Functions 31 | ### 32 | 33 | def isValidHex(hexString : str) -> bool: 34 | '''returns true if string is valid hex''' 35 | # expand on this later 36 | return len(hexString) == 7 37 | 38 | def parseConfig() -> dict[str, Color]: 39 | '''parses palsnap.toml''' 40 | console.log("Defined accent colors:") 41 | accentDict = toml.load(setup.palsnapFile) 42 | # double check values 43 | for key, value in accentDict.items(): 44 | if not isValidHex(value): 45 | raise Exception("palsnap.toml is wrong") 46 | accentDict = {key : hexColor(value) for key, value in accentDict.items()} 47 | console.log(accentDict) 48 | return accentDict 49 | 50 | ### 51 | # Finder Functions 52 | ## 53 | def findBackground(labColors : npt.NDArray[any], mode : str, dominant : int) -> Color: 54 | '''returns the background color for the palette''' 55 | if mode == "auto": 56 | # Apply KMeans clustering 57 | kmeans = KMeans(n_clusters=1) 58 | kmeans.fit(labColors) 59 | bgColors = kmeans.cluster_centers_ 60 | bgColors = tuple(bgColors[0]) 61 | return Color(*bgColors) 62 | else: 63 | # Apply KMeans clustering 64 | console.log(f"Using {dominant} dominant colors to find {mode} background color.") 65 | kmeans = KMeans(n_clusters=dominant) 66 | kmeans.fit(labColors) 67 | bgColors = kmeans.cluster_centers_ 68 | bgColors = [tuple(labColor) for labColor in bgColors] 69 | bgColors = sorted(bgColors, key=lambda x: x[0]) 70 | if mode == "dark": 71 | return Color(*bgColors[0]) 72 | else: 73 | return Color(*bgColors[-1]) 74 | 75 | def findForeground(bgColor : Color, labColors : npt.NDArray[any]) -> Color: 76 | '''returns the foreground color for the palette''' 77 | L = bgColor.L 78 | threshold = 0.33 79 | # Filter out the bad colors 80 | filterArray = np.where(abs(labColors[:,0]-L) > threshold, True, False) 81 | goodColors = labColors[filterArray] 82 | if goodColors.size == 0: 83 | raise Exception("No foreground found. Please lower threshold.") 84 | # Apply KMeans clustering 85 | kmeans = KMeans(n_clusters=1) 86 | kmeans.fit(goodColors) 87 | fgColor = kmeans.cluster_centers_ 88 | fgColor = tuple(fgColor[0]) 89 | fgColor = Color(*fgColor) 90 | return fgColor 91 | 92 | def filterColors(labColors : npt.NDArray[any], specialColor : tuple[int | float, int | float, int | float], numSample : int) -> Color: 93 | '''filter colors in image based on a color''' 94 | L, a, b = specialColor 95 | specialColor = np.asarray([[L, a, b]]) 96 | # Filter to find a certain number of the most similar colors 97 | kd_tree = KDTree(labColors) 98 | dist, idx = kd_tree.query(specialColor, k=numSample) 99 | filteredColors = labColors[idx] 100 | # Eliminate channels 101 | height, width, channels = filteredColors.shape 102 | filteredColors = filteredColors.reshape((height * width, channels)) 103 | # Use KMedoids to find best one out of the sampled colors 104 | distMatrix = pairwise_distances(filteredColors, filteredColors) 105 | dominantIdx = fasterpam(distMatrix, 1) 106 | finalColor = tuple(filteredColors[dominantIdx.medoids][0]) 107 | # Convert to Color 108 | return Color(*finalColor) 109 | 110 | def findAccentColors(labColors : npt.NDArray[any], extraDict : dict[str, Color], numSample : int) -> dict[str, Color]: 111 | '''finds the accent colors''' 112 | resDict = dict() 113 | values = [(key, value.oklab) for key, value in extraDict.items()] 114 | # Get the extra colors in parallel 115 | futureDict = dict() 116 | with concurrent.futures.ThreadPoolExecutor() as executor: 117 | # Find the futures 118 | for key, value in values: 119 | future = executor.submit(filterColors, labColors, value, numSample) 120 | futureDict[future] = key 121 | # Process result 122 | for future in concurrent.futures.as_completed(futureDict): 123 | key = futureDict[future] 124 | result = future.result() 125 | resDict[key] = result 126 | # Rearrange key ordering 127 | keyOrder = list(extraDict.keys()) 128 | resDict = {key : resDict[key] for key in keyOrder} 129 | return resDict 130 | 131 | def findColorHarmony(givenColor : Color) -> dict[str, Color]: 132 | '''finds colors that are harmonious with the given color''' 133 | # use Oklch color space 134 | light, chroma, hue = givenColor.oklch 135 | harmonyColors = dict() 136 | # complementary 137 | comHue = (hue + 180) % 360 138 | complementaryColor = (light, chroma, comHue) 139 | harmonyColors["complementary"] = lchColor(complementaryColor) 140 | # analogous 141 | angHue1 = (hue + 30) % 360 142 | angHue2 = (hue - 30) % 360 143 | ang1 = (light, chroma, angHue1) 144 | ang2 = (light, chroma, angHue2) 145 | harmonyColors["analogous 1"] = lchColor(ang1) 146 | harmonyColors["analogous 2"] = lchColor(ang2) 147 | # split complementary 148 | splitHue1 = (hue + 150) % 360 149 | splitHue2 = (hue + 210) % 360 150 | split1 = (light, chroma, splitHue1) 151 | split2 = (light, chroma, splitHue2) 152 | harmonyColors["split complementary 1"] = lchColor(split1) 153 | harmonyColors["split complementary 2"] = lchColor(split2) 154 | # triadic 155 | triHue1 = (hue + 120) % 360 156 | triHue2 = (hue + 240) % 360 157 | tri1 = (light, chroma, triHue1) 158 | tri2 = (light, chroma, triHue2) 159 | harmonyColors["triadic 1"] = lchColor(tri1) 160 | harmonyColors["triadic 2"] = lchColor(tri2) 161 | # square (og, 90, 180, 270) 162 | squareHue1 = (hue + 90) % 360 163 | squareHue2 = (hue + 270) % 360 164 | sq1 = (light, chroma, squareHue1) 165 | sq2 = (light, chroma, squareHue2) 166 | harmonyColors["square 1"] = lchColor(sq1) 167 | harmonyColors["square 2"] = lchColor(sq2) 168 | # tetradic (og, 60, 180, 240) 169 | tetraHue1 = (hue + 60) % 360 170 | tetraHue2 = (hue + 240) % 360 171 | tetra1 = (light, chroma, tetraHue1) 172 | tetra2 = (light, chroma, tetraHue2) 173 | harmonyColors["tetradic 1"] = lchColor(tetra1) 174 | harmonyColors["tetradic 2"] = lchColor(tetra2) 175 | return harmonyColors 176 | 177 | def findMode(bgColor : Color) -> str: 178 | '''finds the mode of the palette if given mode is auto''' 179 | if bgColor.L >= 0.5: 180 | return "light" 181 | else: 182 | return "dark" 183 | 184 | ### 185 | # Manipulation Functions 186 | ### 187 | def processImage(imgPath : str) -> npt.NDArray[any]: 188 | '''processes the image and returns an array of Oklab colors''' 189 | # Get the colors 190 | console.log(f"Reading image [u]{imgPath}[/u].") 191 | imgPath = os.path.expanduser(imgPath) 192 | rgbColors = read_image(imgPath, method="Imageio") 193 | rgbColors = rgbColors[..., 0:3] 194 | # Convert colors to Oklab color space 195 | console.log("Converting image colors to [cyan]Oklab[/cyan] color space.") 196 | xyzColors = sRGB_to_XYZ(rgbColors) 197 | labColors = XYZ_to_Oklab(xyzColors) 198 | labColors = labColors.reshape((-1,3)) 199 | return labColors 200 | 201 | def adjustAccents(colorDict : dict[str, Color], iterations : int, weight : int) -> dict[str, Color]: 202 | '''adjusts lightness of accents based on background''' 203 | # based on https://github.com/jan-warchol/selenized/blob/master/balancing-lightness-of-colors.md 204 | bgLightness = colorDict["bg"].cielab[0] 205 | # adjust the lightness 206 | newAccents = performOptimal(bgLightness, colorDict, iterations, weight) 207 | return newAccents 208 | 209 | def findClosestColor(colorDict : dict[str, Color], labColor : Color) -> str: 210 | '''finds the color name in colorDict that labColor is closest to''' 211 | # find the best color name 212 | bestColor = None 213 | bestDist = float("inf") 214 | for (key, otherColor) in colorDict.items(): 215 | distance = Color.findDist(labColor, otherColor) 216 | if distance < bestDist: 217 | bestDist = distance 218 | bestColor = key 219 | return bestColor 220 | 221 | def tweakColor(paletteColor : Color, orgColor : Color, hueThreshold, hueFactor, chromaThreshold, chromaFactor) -> Color: 222 | '''tweaks the chroma and hue of the color so that it is closer to the accent colors''' 223 | orgHue = orgColor.oklch[2] 224 | paletteHue = paletteColor.oklch[2] 225 | orgChroma = orgColor.oklch[1] 226 | paletteChroma = paletteColor.oklch[1] 227 | # change hue 228 | if abs(orgHue - paletteHue) > hueThreshold: 229 | newHue = orgHue * hueFactor 230 | else: 231 | newHue = paletteHue 232 | # change chroma 233 | if paletteChroma * chromaThreshold <= orgChroma: 234 | newChroma = orgChroma * chromaFactor 235 | else: 236 | newChroma = paletteChroma 237 | return lchColor((paletteColor.oklch[0], newChroma, newHue)) 238 | 239 | ### 240 | # Palette Functions 241 | ### 242 | def pickColors(imgPath : str, accentColors : dict[str, Color], mode : str, dominant : int, numSample : int) -> dict[str, Color]: 243 | '''start picking colors from the image''' 244 | palette = dict() 245 | labColors = processImage(imgPath) 246 | # Background 247 | console.log("Finding background color.") 248 | bg = findBackground(labColors, mode, dominant) 249 | palette["bg"] = bg 250 | # Foreground 251 | console.log("Finding foreground color.") 252 | fg = findForeground(bg, labColors) 253 | palette["fg"] = fg 254 | # Accents 255 | console.log(f"Finding accents by sampling [magenta]{numSample} colors[/magenta].") 256 | accentColors = findAccentColors(labColors, accentColors, numSample) 257 | palette |= accentColors 258 | return palette 259 | 260 | def expandPalette(accentColors : dict[str, Color], givenColor : Color, palette : dict[str, list[Color]]) -> dict[str, list[Color]]: 261 | '''expand the modified palette with a given color by finding the corresponding label via accentColors''' 262 | harmonyColors = findColorHarmony(givenColor) 263 | # find closest color for each of the harmony colors and expand the palette 264 | for key in harmonyColors: 265 | # find the closest label 266 | currColor = harmonyColors[key] 267 | label = findClosestColor(accentColors, currColor) 268 | # expand the palette 269 | palette[label].append(currColor) 270 | return palette 271 | 272 | def exportPalette(colorDict : dict[str, Color]) -> None: 273 | '''exports the palette''' 274 | strDict = {key: value for key, value in colorDict.items() if key == "image" or key == "mode"} 275 | colorDict : dict[str, str] = {key : value.hex for key, value in colorDict.items() if key != "image" and key != "mode"} 276 | colorDict = strDict | colorDict 277 | exportPath = os.path.join(setup.cache, "palette.toml") 278 | with open(exportPath, "w") as file: 279 | toml.dump(colorDict, file) 280 | file.close() 281 | 282 | def mixPalette(accentColors : dict[str, Color], palette : dict[str, Color], mixAmount : float, mixThreshold : float) -> dict[str, Color]: 283 | '''mixes colors in palette with accent colors''' 284 | console.log(f"Mixing colors to increase color variety.") 285 | console.log(f"Mixing colors by {mixAmount * 100}% each iteration with threshold distance {mixThreshold}.") 286 | numNeeded = len(accentColors.keys()) 287 | passed = set() 288 | counter = 0 289 | while len(passed) != numNeeded: 290 | for key in accentColors: 291 | counter += 1 292 | paletteColor = palette[key].rgb 293 | accentColor = accentColors[key].rgb 294 | distance = Color.findDist(palette[key], accentColors[key]) 295 | # only mix by 10% if distance > 0.16 296 | if distance > mixThreshold: 297 | mixedColor = mixbox.lerp(paletteColor, accentColor, mixAmount) 298 | # set mixed colors in palette 299 | mixedColor = rgbColor(mixedColor) 300 | palette[key] = mixedColor 301 | else: 302 | passed.add(key) 303 | console.log(f"Mixed a total of {counter} times.") 304 | return palette 305 | 306 | ### 307 | # Main Function 308 | ### 309 | def extractPalette(imgPath : str, mode : str, dominant : int, extraFlag : bool, mixFlag : bool, tweakFlag : bool, 310 | numSample : int, mixAmount : float, mixThreshold : float, iterations : int, weight : int, 311 | hueThreshold, hueFactor, chromaThreshold, chromaFactor, adjust : bool) -> dict[str, Color]: 312 | '''extracts the palette from the image''' 313 | # Get the accent values 314 | console.log("Reading [u]palsnap.toml[/u].") 315 | accentColors = parseConfig() 316 | 317 | console.log("Extracting palette.") 318 | palette = pickColors(imgPath, accentColors, mode, dominant, numSample) 319 | 320 | # harmony colors 321 | if extraFlag: 322 | console.log("Finding extra colors to improve color variety.") 323 | bgColor = palette["bg"] 324 | fgColor = palette["fg"] 325 | palette = {key: [value] for key, value in palette.items()} 326 | console.log("Getting colors harmonious to background.") 327 | paletteExpanded = expandPalette(accentColors, bgColor, palette) 328 | console.log("Getting colors harmonious to foreground.") 329 | paletteExpanded = expandPalette(accentColors, fgColor, paletteExpanded) 330 | # pick the color in the expanded palette with the least difference 331 | for key in paletteExpanded: 332 | possibleColors = paletteExpanded[key] 333 | if len(possibleColors) == 1: 334 | paletteExpanded[key] = possibleColors[0] 335 | else: 336 | orgColor = accentColors[key] 337 | # find the best lab color 338 | bestColor = None 339 | bestDist = float("inf") 340 | for currColor in possibleColors: 341 | distance = Color.findDist(orgColor, currColor) 342 | if distance < bestDist: 343 | bestColor = currColor 344 | bestDist = distance 345 | paletteExpanded[key] = bestColor 346 | # overwrite palette 347 | palette : dict[str, Color] = paletteExpanded 348 | 349 | # mixing 350 | if mixFlag: 351 | palette = mixPalette(accentColors, palette, mixAmount, mixThreshold) 352 | 353 | # tweak colors 354 | if tweakFlag: 355 | console.log("Tweaking colors.") 356 | for key in accentColors: 357 | newColor = tweakColor(palette[key], accentColors[key], hueThreshold, hueFactor, chromaThreshold, chromaFactor) 358 | palette[key] = newColor 359 | 360 | # adjusting 361 | if adjust: 362 | console.log(f"Adjusting palette with uniqueness weight {weight}.") 363 | newPalette = adjustAccents(palette, iterations, weight) 364 | palette = {**palette, **newPalette} 365 | 366 | # add image path and mode 367 | palette["image"] = imgPath 368 | if mode == "auto": 369 | palette["mode"] = findMode(palette["bg"]) 370 | else: 371 | palette["mode"] = mode 372 | 373 | # generate background gradient 374 | console.log("Generating background gradient.") 375 | bgGradient = findBgGradient(palette["bg"], palette["fg"]) 376 | palette |= bgGradient 377 | 378 | # reorder palette 379 | bgOrder = ['image', 'mode', 'bg', 'fg', 'bg1', 'bg2', 'bg3', 'bg4', 'bg5'] 380 | reorderPalette = {key: palette[key] for key in bgOrder} 381 | reorderPalette.update({key: value for key, value in palette.items() if key not in reorderPalette}) 382 | palette = reorderPalette 383 | 384 | # display palette 385 | console.log("Final palette:") 386 | console.log(palette) 387 | 388 | # export palette for preview 389 | console.log("Exporting palette.") 390 | exportPalette(palette) 391 | 392 | return palette -------------------------------------------------------------------------------- /palettesnap/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EmperorEntropy/PaletteSnap/f5f3ebc1716fe1085d7090dde07dd5a1cee5ba57/palettesnap/__init__.py -------------------------------------------------------------------------------- /palettesnap/background.py: -------------------------------------------------------------------------------- 1 | ### 2 | # Modules 3 | ### 4 | 5 | # Internal Modules 6 | from .colorClass import Color 7 | 8 | # External Modules 9 | import numpy as np 10 | 11 | def findBgGradient(bgColor : Color, fgColor : Color) -> dict[str, Color]: 12 | '''returns a spectrum''' 13 | '''determines background accent''' 14 | bgDict = dict() 15 | # Get 5 gradient colors from background to foreground 16 | gradient = np.linspace(bgColor.oklab, fgColor.oklab, 7) 17 | gradient = [Color(*tuple(labColor)) for labColor in gradient] 18 | for i in range(1, 6): 19 | bgDict[f"bg{i}"] = gradient[i] 20 | return bgDict -------------------------------------------------------------------------------- /palettesnap/cache.py: -------------------------------------------------------------------------------- 1 | ### 2 | # Modules 3 | ### 4 | 5 | # External modules 6 | import os 7 | import toml 8 | from typer import confirm, Exit 9 | from random import choice 10 | 11 | # Internal modules 12 | from . import setup 13 | from .console import console 14 | from .colorClass import Color, hexColor 15 | from .preview import readPalette 16 | from .wallpaper import setWallpaper 17 | from .templating import exportAll 18 | 19 | ### 20 | # Cache functions 21 | ### 22 | def cacheSet(name : str) -> None: 23 | '''caches the current palette by creating the file palette in the cache dir''' 24 | console.log(f"Caching current palette to {name}.toml.") 25 | if name in ["palette"] or '.' in name: 26 | console.log("Illegal name. Please try a different name.") 27 | raise Exit() 28 | else: 29 | # check if name already exists 30 | filePath= os.path.join(setup.cache, f"{name}.toml") 31 | if os.path.exists(filePath): 32 | console.log(f"Palette with name {name} already exists in cached directory.") 33 | overwrite = confirm("Do you want to overwrite it?") 34 | if overwrite: 35 | palette = readPalette() 36 | with open(filePath, "w") as file: 37 | toml.dump(palette, file) 38 | file.close() 39 | console.log("Cache operation [green]completed[/green].") 40 | else: 41 | console.log("Cache operation [red]canceled[/red].") 42 | raise Exit() 43 | else: 44 | palette = readPalette() 45 | with open(filePath, "w") as file: 46 | toml.dump(palette, file) 47 | file.close() 48 | console.log("Cache operation [green]completed[/green].") 49 | 50 | def loadCache(name : str) -> None: 51 | '''loads the palette from the file path in the cache dir''' 52 | cacheDir = setup.cache 53 | if name == "palette": 54 | console.log("Illegal name. Please try a different name.") 55 | raise Exit() 56 | else: 57 | console.log(f"Loading cached {name}.toml.") 58 | filePath = os.path.join(cacheDir, f"{name}.toml") 59 | # check if palette exists 60 | if not os.path.exists(filePath): 61 | console.log(f"No palette with name {name} exists in cache.") 62 | console.log("[red]Failed[/red] to load cached palette.") 63 | raise Exit() 64 | else: 65 | # get the palette 66 | textPalette : dict[str, str] = toml.load(filePath) 67 | imgDict = {"image" : textPalette["image"], "mode" : textPalette["mode"]} 68 | palette : dict[str, Color] = {key: hexColor(value) for key, value in textPalette.items() if key != "image" and key != "mode"} 69 | palette = imgDict | palette 70 | console.log("Cached file [green]succesfully[/green] loaded.") 71 | # overwrite palette.toml for previewing 72 | currPalette = os.path.join(setup.cache, "palette.toml") 73 | with open(currPalette, "w") as file: 74 | toml.dump(textPalette, file) 75 | file.close() 76 | # set wallpaper background 77 | imgPath = palette["image"] 78 | setWallpaper(imgPath) 79 | # export templates 80 | exportAll(palette) 81 | 82 | def loadRandomCache(): 83 | '''loads a random cached palette''' 84 | cacheDir = setup.cache 85 | console.log("Randomly picking a cached palette.") 86 | # pick a random cached palette 87 | exclude = ["palette.toml", "PaletteTest.html", "styles.css"] 88 | allFiles = os.listdir(cacheDir) 89 | cachedPalettes = [file for file in allFiles if file not in exclude] 90 | fileName = choice(cachedPalettes) 91 | console.log(f"Cached palette {fileName} chosen.") 92 | # load it 93 | name = os.path.splitext(fileName)[0] 94 | loadCache(name) 95 | 96 | 97 | def removeCache(names : list[str]) -> None: 98 | '''removes cache palettes''' 99 | cacheDir = setup.cache 100 | if "palette" in names: 101 | console.log("palette is an illegal name. It cannot be removed.") 102 | else: 103 | for name in names: 104 | filePath= os.path.join(cacheDir, f"{name}.toml") 105 | os.remove(filePath) 106 | console.log(f"Cached file {name} has been [green]succesfully[/green] cleared.") 107 | 108 | def clearCache() -> None: 109 | '''clears the cache''' 110 | cacheDir = setup.cache 111 | exclude = ["palette.toml", "PaletteTest.html", "styles.css"] 112 | for fileName in os.listdir(cacheDir): 113 | filePath = os.path.join(cacheDir, f"{fileName}") 114 | if os.path.isfile(filePath) and fileName not in exclude: 115 | os.remove(filePath) 116 | console.log("Cache directory has been [green]succesfully[/green] cleared.") 117 | 118 | def listCache() -> None: 119 | '''counts and lists the cached palettes''' 120 | cacheDir = setup.cache 121 | exclude = ["palette.toml", "PaletteTest.html", "styles.css"] 122 | allFiles = os.listdir(cacheDir) 123 | cachedPalettes = [file for file in allFiles if file not in exclude] 124 | console.log(f"There are {len(cachedPalettes)} cached palettes:") 125 | for file in cachedPalettes: 126 | console.log(f"{file}") 127 | return None 128 | 129 | def renameCache(oldName : str, newName : str) -> None: 130 | '''renames cached palette''' 131 | cacheDir = setup.cache 132 | if oldName == "palette" or newName == "palette" or '.' in oldName or '.' in newName: 133 | console.log("Illegal name. Please use different name.") 134 | raise Exit() 135 | else: 136 | oldPath = os.path.join(cacheDir, f"{oldName}.toml") 137 | newPath = os.path.join(cacheDir, f"{newName}.toml") 138 | os.rename(oldPath, newPath) 139 | console.log(f"Renamed {oldName}.toml to {newName}.toml.") 140 | 141 | def checkCache(name : str) -> bool: 142 | '''checks a cached palette and makes sure it is legal''' 143 | currPalette = list(readPalette().keys()) 144 | palettePath = os.path.join(setup.cache, f"{name}") 145 | cachedPalette = list(toml.load(palettePath).keys()) 146 | if currPalette == cachedPalette: 147 | return True 148 | else: 149 | return False 150 | 151 | def checkAll() -> None: 152 | '''checks all cached palettes and returns list of illegal ones''' 153 | # Get all cached palettes 154 | cacheDir = setup.cache 155 | exclude = ["palette.toml", "PaletteTest.html", "styles.css"] 156 | allFiles = os.listdir(cacheDir) 157 | cachedPalettes = [file for file in allFiles if file not in exclude] 158 | illegalPalettes = [palette for palette in cachedPalettes if not checkCache(palette)] 159 | count = len(illegalPalettes) 160 | if len(illegalPalettes) == 0: 161 | console.log("All palettes are well-defined.") 162 | else: 163 | console.log(f"[red]{count}[/red] palettes with invaild palette variable names found.") 164 | console.log(f"{illegalPalettes}") 165 | 166 | -------------------------------------------------------------------------------- /palettesnap/cli.py: -------------------------------------------------------------------------------- 1 | ### 2 | # Modules 3 | ### 4 | 5 | # Internal Modules 6 | from . import setup 7 | from .PaletteSnap import extractPalette 8 | from .wallpaper import setWallpaper 9 | from .templating import exportAll 10 | from .console import console 11 | from .preview import previewPalette 12 | from .cache import cacheSet, loadCache, loadRandomCache, removeCache, clearCache, listCache, renameCache, checkAll 13 | from .outdatedCheck import outdatedCheck 14 | 15 | # External Modules 16 | import typer 17 | from typing_extensions import Annotated 18 | import time 19 | 20 | ### 21 | # Functions 22 | ### 23 | app = typer.Typer(context_settings={"help_option_names": ["-h", "--help"]}) 24 | cache_app = typer.Typer(context_settings={"help_option_names": ["-h", "--help"]}, help="Manipulates the cache.") 25 | app.add_typer(cache_app, name="cache") 26 | 27 | # gen command 28 | @app.command() 29 | def gen( 30 | path: Annotated[ 31 | str, 32 | typer.Argument(help="Path to wallpaper image.") 33 | ], 34 | skip: Annotated[ 35 | bool, 36 | typer.Option( 37 | help="Skip creating templates and setting wallpaper.", rich_help_panel="Options" 38 | ), 39 | ] = False, 40 | mode : Annotated[ 41 | str, 42 | typer.Option( 43 | help="Theme mode. light, dark, or auto.", rich_help_panel="Options" 44 | ), 45 | ] = "auto", 46 | dominant : Annotated[ 47 | int, 48 | typer.Option( 49 | help="Number of dominant colors to pick from image for background. Must be >= 2.", rich_help_panel="Options" 50 | ), 51 | ] = 5, 52 | extra: Annotated[ 53 | bool, 54 | typer.Option( 55 | help="Toggles extra colors.", rich_help_panel="Variety" 56 | ), 57 | ] = False, 58 | mix: Annotated[ 59 | bool, 60 | typer.Option( 61 | help="Toggles extra colors.", rich_help_panel="Variety" 62 | ), 63 | ] = False, 64 | tweak: Annotated[ 65 | bool, 66 | typer.Option( 67 | help="Toggles extra colors.", rich_help_panel="Variety" 68 | ), 69 | ] = False, 70 | sample : Annotated[ 71 | int, 72 | typer.Option( 73 | help="Number of colors to sample from images to find accent colors.", 74 | rich_help_panel="Options" 75 | ), 76 | ] = 10000, 77 | mixAmount: Annotated[ 78 | float, 79 | typer.Option( 80 | "--mixAmount", "-ma", 81 | help="Percentage amount accent color should be mixed with picked color. 0 to 1.", rich_help_panel="Mix Settings" 82 | ), 83 | ] = 0.1, 84 | mixThreshold : Annotated[ 85 | float, 86 | typer.Option( 87 | "--mixThreshold", "-mt", 88 | help="Distance threshold for mixing.", rich_help_panel="Mix Settings" 89 | ), 90 | ] = 0.16, 91 | iterations : Annotated[ 92 | int, 93 | typer.Option( 94 | "--iterations", "-i", 95 | help="Number of iterations for optimization to run.", 96 | rich_help_panel="Options" 97 | ), 98 | ] = 10000, 99 | weight : Annotated[ 100 | int, 101 | typer.Option( 102 | help="Uniqueness weight for optimization. Larger means more unique.", 103 | rich_help_panel="Options" 104 | ), 105 | ] = 100, 106 | cache : Annotated[ 107 | str, 108 | typer.Option( 109 | help="Name of palette to be cached as.", 110 | rich_help_panel="Options" 111 | ), 112 | ] = None, 113 | hueThreshold : Annotated[ 114 | float, 115 | typer.Option( 116 | "--hueThreshold", "-ht", 117 | help="Distance threshold for hue.", rich_help_panel="Tweak Settings" 118 | ), 119 | ] = 10.0, 120 | hueFactor : Annotated[ 121 | float, 122 | typer.Option( 123 | "--hueFactor", "-hf", 124 | help="Factor of defined accent color hue. 0 to 1.", rich_help_panel="Tweak Settings" 125 | ), 126 | ] = 1.0, 127 | chromaThreshold : Annotated[ 128 | float, 129 | typer.Option( 130 | "--chromaThreshold", "-ct", 131 | help="Threshold for chroma.", rich_help_panel="Tweak Settings" 132 | ), 133 | ] = 3.0, 134 | chromaFactor : Annotated[ 135 | float, 136 | typer.Option( 137 | "--chromaFactor", "-cf", 138 | help="Factor of defined accent color chroma. 0 to 1.", rich_help_panel="Tweak Settings" 139 | ), 140 | ] = 0.25, 141 | ): 142 | ''' 143 | Generates color palette given path to image and optional arguments. 144 | ''' 145 | outdatedCheck() 146 | # precheck 147 | if mode not in ["auto", "light", "dark"]: 148 | raise typer.BadParameter(f"{mode} is not a valid mode. Allowed values are auto, light, and dark.") 149 | if dominant <= 1: 150 | console.log("Illegal number of dominant colors.") 151 | if mode == "auto" and dominant != 5: 152 | console.log("Mode must not be auto for dominant option to be used.") 153 | # functionality 154 | if skip: 155 | start = time.time() 156 | # Only create palette 157 | palette = extractPalette(path, mode, dominant, extra, mix, tweak, sample, mixAmount, mixThreshold, iterations, weight, hueThreshold, hueFactor, chromaThreshold, chromaFactor, True) 158 | # cache 159 | if cache is not None: 160 | cacheSet(cache) 161 | end = time.time() 162 | console.log(f"Process [green]completed[/green] in {end-start} seconds.") 163 | else: 164 | start = time.time() 165 | # extract palette 166 | palette = extractPalette(path, mode, dominant, extra, mix, tweak, sample, mixAmount, mixThreshold, iterations, weight, hueThreshold, hueFactor, chromaThreshold, chromaFactor, True) 167 | # set wallpaper background 168 | setWallpaper(path) 169 | # export templates 170 | exportAll(palette) 171 | # cache 172 | if cache is not None: 173 | cacheSet(cache) 174 | end = time.time() 175 | console.log(f"Process [green]completed[/green] in {end-start} seconds.") 176 | 177 | # preview command 178 | @app.command() 179 | def preview( 180 | image : Annotated[ 181 | bool, 182 | typer.Option( 183 | help="Flag for image in preview output.", 184 | rich_help_panel="Customization" 185 | ), 186 | ] = True, 187 | ): 188 | '''Previews current color palette.''' 189 | outdatedCheck() 190 | previewPalette(image) 191 | 192 | ### 193 | # cache command 194 | ### 195 | 196 | @cache_app.command("set") 197 | def cache_set( 198 | name: Annotated[ 199 | str, 200 | typer.Argument(help="Name of cached palette to set to.") 201 | ], 202 | ): 203 | '''Caches the current color palette with the given name.''' 204 | start = time.time() 205 | outdatedCheck() 206 | cacheSet(name) 207 | end = time.time() 208 | console.log(f"Process [green]completed[/green] in {end-start} seconds.") 209 | 210 | @cache_app.command("load") 211 | def cache_load( 212 | name: Annotated[ 213 | str, 214 | typer.Argument(help="Name of cached palette.") 215 | ], 216 | ): 217 | '''Loads the cached palette given a name.''' 218 | start = time.time() 219 | outdatedCheck() 220 | loadCache(name) 221 | end = time.time() 222 | console.log(f"Process [green]completed[/green] in {end-start} seconds.") 223 | 224 | @cache_app.command("random") 225 | def cache_random(): 226 | '''Loads a random cached palette.''' 227 | start = time.time() 228 | outdatedCheck() 229 | loadRandomCache() 230 | end = time.time() 231 | console.log(f"Process [green]completed[/green] in {end-start} seconds.") 232 | 233 | @cache_app.command("remove") 234 | def cache_remove( 235 | names: Annotated[ 236 | list[str], 237 | typer.Argument(help="Names of cached palette.") 238 | ], 239 | ): 240 | '''Removes cached palettes.''' 241 | start = time.time() 242 | outdatedCheck() 243 | removeCache(names) 244 | end = time.time() 245 | console.log(f"Process [green]completed[/green] in {end-start} seconds.") 246 | 247 | @cache_app.command("clear") 248 | def cache_clear( 249 | skip: Annotated[ 250 | bool, 251 | typer.Option(help="Skips the confirmation prompt.") 252 | ] = False, 253 | ): 254 | '''Clears all cached palettes.''' 255 | start = time.time() 256 | outdatedCheck() 257 | if not skip: 258 | prompt = typer.confirm("Are you sure you want to clear the entire cache?") 259 | if prompt: 260 | clearCache() 261 | else: 262 | console.log() 263 | raise typer.Exit() 264 | else: 265 | clearCache() 266 | end = time.time() 267 | console.log(f"Process [green]completed[/green] in {end-start} seconds.") 268 | 269 | @cache_app.command("list") 270 | def cache_list(): 271 | '''Lists all cached palettes.''' 272 | start = time.time() 273 | outdatedCheck() 274 | listCache() 275 | end = time.time() 276 | console.log(f"Process [green]completed[/green] in {end-start} seconds.") 277 | 278 | @cache_app.command("rename") 279 | def cache_rename( 280 | oldName: Annotated[ 281 | str, 282 | typer.Argument(help="Present name of cached palette.") 283 | ], 284 | newName : Annotated[ 285 | str, 286 | typer.Argument(help="New name for cached palette.") 287 | ], 288 | ): 289 | '''Renames a cached palette.''' 290 | start = time.time() 291 | outdatedCheck() 292 | renameCache(oldName, newName) 293 | end = time.time() 294 | console.log(f"Process [green]completed[/green] in {end-start} seconds.") 295 | 296 | @cache_app.command("check") 297 | def cache_check(): 298 | '''Checks all the cached palettes.''' 299 | start = time.time() 300 | outdatedCheck() 301 | checkAll() 302 | end = time.time() 303 | console.log(f"Process [green]completed[/green] in {end-start} seconds.") 304 | -------------------------------------------------------------------------------- /palettesnap/colorClass.py: -------------------------------------------------------------------------------- 1 | ### 2 | # Modules 3 | ### 4 | import numpy as np 5 | from colour import Oklab_to_XYZ, XYZ_to_sRGB, RGB_to_HSL, sRGB_to_XYZ, XYZ_to_Lab, XYZ_to_Oklab, Lab_to_XYZ, HSL_to_RGB 6 | from colour.models import JCh_to_Jab, Jab_to_JCh 7 | from PIL import ImageColor 8 | 9 | ### 10 | # Helper functions 11 | ### 12 | def okToNormalRgb(okColor): 13 | '''oklab color to rgb normalized''' 14 | xyzColor = Oklab_to_XYZ(list(okColor)) 15 | rgbColor = XYZ_to_sRGB(xyzColor) 16 | red, green, blue = tuple(rgbColor) 17 | # Clamp color values to eliminate impossible colors 18 | [red, green, blue] = np.clip([red, green, blue], 0, 1) 19 | return (red, green, blue) 20 | 21 | def normalRgbToRgb(rgbColor): 22 | '''normalized rgb to rgb''' 23 | red, green, blue = rgbColor 24 | red = round(red * 255) 25 | green = round(green * 255) 26 | blue = round(blue * 255) 27 | return (red, green, blue) 28 | 29 | def normalRgbToHex(rgbColor): 30 | '''normalized rgb to hex''' 31 | red, green, blue = normalRgbToRgb(rgbColor) 32 | return "#{:02x}{:02x}{:02x}".format(red,green,blue) 33 | 34 | def normalRgbToHsl(rgbColor): 35 | '''normalized rgb to hsl''' 36 | hslColor = RGB_to_HSL(rgbColor) 37 | hue, saturation, light = tuple(hslColor) 38 | hue = round(hue * 360) 39 | saturation = round(saturation * 100) 40 | light = round(light * 100) 41 | return (hue, saturation, light) 42 | 43 | 44 | def normalRgbToCIE(rgbColor): 45 | '''normalized rgb to cielab''' 46 | xyzColor = sRGB_to_XYZ(list(rgbColor)) 47 | cieColor = XYZ_to_Lab(xyzColor) 48 | return cieColor 49 | 50 | # used for palette extraction 51 | def hexToLab(hexColor): 52 | '''converts hex to lab color''' 53 | red, green, blue = ImageColor.getcolor(hexColor, "RGB") 54 | red /= 255 55 | green /= 255 56 | blue /= 255 57 | xyzColors = sRGB_to_XYZ([red, green, blue]) 58 | labColors = XYZ_to_Oklab(xyzColors) 59 | return tuple(labColors) 60 | 61 | # used for palette extraction 62 | def hslColor(hslColor): 63 | hue, saturation, light = hslColor 64 | hue /= 360 65 | saturation /= 100 66 | light /= 100 67 | rgbColor = HSL_to_RGB([hue, saturation, light]) 68 | xyzColor = sRGB_to_XYZ(rgbColor) 69 | labColor = tuple(XYZ_to_Oklab(xyzColor)) 70 | return Color(*labColor) 71 | 72 | # used for optimization 73 | def cieColor(currColor): 74 | '''CIE Lab color as tuple to Color class''' 75 | xyzColor = Lab_to_XYZ(list(currColor)) 76 | okLab = tuple(XYZ_to_Oklab(xyzColor)) 77 | okColor = Color(*okLab) 78 | return okColor 79 | 80 | # used for palette extraction 81 | def rgbColor(rgbColor): 82 | '''rgb color to Color''' 83 | red, green, blue = rgbColor 84 | red /= 255 85 | green /= 255 86 | blue /= 255 87 | xyzColor = sRGB_to_XYZ([red, green, blue]) 88 | labColor = tuple(XYZ_to_Oklab(xyzColor)) 89 | return Color(*labColor) 90 | 91 | # use for palette extraction 92 | def hexColor(hexColor): 93 | '''hex color to Color''' 94 | red, green, blue = ImageColor.getcolor(hexColor, "RGB") 95 | red /= 255 96 | green /= 255 97 | blue /= 255 98 | xyzColors = sRGB_to_XYZ([red, green, blue]) 99 | labColors = tuple(XYZ_to_Oklab(xyzColors)) 100 | return Color(*labColors) 101 | 102 | # used for previewing 103 | def hexToRgb(hexColor): 104 | red, green, blue = ImageColor.getcolor(hexColor, "RGB") 105 | return (red, green, blue) 106 | 107 | # used for palette extraction 108 | def lchColor(lchColor): 109 | '''Oklch to Color''' 110 | labColor = tuple(JCh_to_Jab(list(lchColor))) 111 | return Color(*labColor) 112 | 113 | ### 114 | # Color class 115 | ### 116 | class Color(): 117 | 118 | def __init__(self, L, a, b): 119 | '''Initialize Oklab color''' 120 | self.L = L 121 | self.a = a 122 | self.b = b 123 | # different color spaces 124 | self.oklab = (L, a, b) 125 | self.oklch = tuple(Jab_to_JCh(list(self.oklab))) 126 | self.normalRgb = okToNormalRgb(self.oklab) 127 | self.rgb = normalRgbToRgb(self.normalRgb) 128 | # we use rgb since rgb eliminates impossible colors 129 | self.hex = normalRgbToHex(self.normalRgb) 130 | self.hsl = normalRgbToHsl(self.normalRgb) 131 | self.cielab = normalRgbToCIE(self.normalRgb) 132 | 133 | def __repr__(self): 134 | '''string representation''' 135 | red, green, blue = self.rgb 136 | return f"{self.hex} \033[48;2;{red};{green};{blue}m \033[0m" 137 | 138 | def findDist(self, other): 139 | '''computes distance between two colors''' 140 | return ((self.L - other.L)**2+(self.a - other.a)**2+(self.b - other.b)**2)**0.5 141 | 142 | def lighten(self, factor : float): 143 | '''lightens color by factor percentage''' 144 | factor = 1 + factor 145 | L, a, b = self.oklab 146 | L *= factor 147 | return Color(L, a, b) 148 | 149 | def darken(self, factor : float): 150 | '''darkens color by factor percentage''' 151 | factor = 1 - factor 152 | L, a, b = self.oklab 153 | L *= factor 154 | return Color(L, a, b) -------------------------------------------------------------------------------- /palettesnap/colorOptimize.py: -------------------------------------------------------------------------------- 1 | ### 2 | # Modules 3 | ### 4 | 5 | # External modules 6 | import numpy as np 7 | import math 8 | from scipy.optimize import minimize, Bounds 9 | import warnings 10 | 11 | # Internal modules 12 | from .console import console 13 | from .colorClass import Color, cieColor 14 | 15 | 16 | ### 17 | # Helper Functions 18 | ### 19 | 20 | # Penalty function 21 | def penalty_function(a, original, weight): 22 | # Distances 23 | deviation_penalty = math.sqrt(np.sum((a - original) ** 2)) 24 | # Uniqueness cost 25 | uniqueness_penalty = np.sum([max(0, 0.1 - abs(a[i] - a[j]))**2 for i in range(len(a)) for j in range(i + 1, len(a))]) 26 | return deviation_penalty + uniqueness_penalty * weight # Higher means more uniqueness 27 | 28 | def no_penalty_function(a, original): 29 | return 0 30 | 31 | # Constraints: 32 | def constraint_1(x): 33 | def inner_constraint(a): 34 | return np.abs(a - x) - 33 # Ensure abs(a_i - x) >= 33 35 | return inner_constraint 36 | 37 | def constraint_2(a): 38 | # Each pair of a_i must differ by at most 20 39 | n = len(a) 40 | constraints = [] 41 | for i in range(n): 42 | for j in range(i + 1, n): 43 | constraints.append(20 - abs(a[i] - a[j])) 44 | return np.array(constraints) 45 | 46 | ### 47 | # Primary Function 48 | ### 49 | 50 | # Ignore warnings 51 | warnings.filterwarnings("ignore", message="delta_grad == 0.0. Check if the approximated function is linear.") 52 | warnings.filterwarnings("ignore", message="Singular Jacobian matrix. Using SVD decomposition to perform the factorizations.") 53 | 54 | def performOptimal(bgLight : int | float, palette : dict[str, Color], iterations : int, weight : int) -> dict[str, Color]: 55 | '''performs optimization on the lightness of the accent colors''' 56 | # set up initial values 57 | lightDict = {key: value for key, value in palette.items() if "bg" not in key} 58 | lightList = [lightDict[key].cielab[0] for key in lightDict] 59 | original_values = lightList.copy() 60 | length = len(original_values) 61 | bounds = Bounds([0] * length, [100] * length) # bounds between 0 and 100 62 | # Constraints 63 | cons = [{'type': 'ineq', 'fun': constraint_1(bgLight)}, 64 | {'type': 'ineq', 'fun': constraint_2}] 65 | # extract and print result 66 | console.log(f"Optimizing palette colors for good contrast with {iterations} as maximum number of iterations.") 67 | result = minimize(penalty_function, lightList, args=(original_values, weight), bounds=bounds, constraints=cons, method='trust-constr', options={'disp': False, 'maxiter': iterations}) 68 | if result.success: 69 | console.log(f"Optimization [green]succeed[/green].") 70 | optimized_values = result.x 71 | # return new accent colors 72 | newLightDict = dict(zip(lightDict.keys(), optimized_values)) 73 | newAccents = dict() 74 | for key in lightDict: 75 | newColor = (newLightDict[key], lightDict[key].cielab[1], lightDict[key].cielab[2]) 76 | newAccents[key] = cieColor(newColor) 77 | return newAccents 78 | else: 79 | console.log("Current optimization method [red]failed[/red].") 80 | console.log("Trying new optimization method.") 81 | # Redo bounds and constraints 82 | fgLight = palette["fg"].cielab[0] 83 | if fgLight > bgLight: 84 | lightThreshold = bgLight + 33 85 | newBounds = Bounds([lightThreshold] * length, [100] * length) 86 | else: 87 | lightThreshold = bgLight - 33 88 | newBounds = Bounds([0] * length, [lightThreshold] * length) 89 | newCons = [{'type': 'ineq', 'fun': constraint_2}] 90 | # Try it again 91 | result = minimize(penalty_function, lightList, args=(original_values, weight), bounds=newBounds, constraints=newCons, method='trust-constr', options={'disp': False, 'maxiter': iterations}) 92 | if result.success: 93 | console.log("Backup optimization [green]succeed[/green].") 94 | optimized_values = result.x 95 | # return new accent colors 96 | newLightDict = dict(zip(lightDict.keys(), optimized_values)) 97 | newAccents = dict() 98 | for key in lightDict: 99 | newColor = (newLightDict[key], lightDict[key].cielab[1], lightDict[key].cielab[2]) 100 | newAccents[key] = cieColor(newColor) 101 | return newAccents 102 | else: 103 | console.log("Backup optimization [red]failed[/red].") 104 | console.log("Manual optimization process started.") 105 | console.log("Palette results from manual operation will not be as good as automatic optimization.") 106 | console.log("Please adjust your settings to prevent manual optimization.") 107 | console.log("Defining the [b]mode[/b] of your image usually prevents manual optimization.") 108 | fgLight = palette["fg"].cielab[0] 109 | if fgLight > bgLight: 110 | lightThreshold = bgLight + 33 111 | else: 112 | lightThreshold = bgLight - 33 113 | optimized_values = [lightThreshold] * length 114 | # return new accent colors 115 | newLightDict = dict(zip(lightDict.keys(), optimized_values)) 116 | newAccents = dict() 117 | for key in lightDict: 118 | newColor = (newLightDict[key], lightDict[key].cielab[1], lightDict[key].cielab[2]) 119 | newAccents[key] = cieColor(newColor) 120 | return newAccents -------------------------------------------------------------------------------- /palettesnap/console.py: -------------------------------------------------------------------------------- 1 | ### 2 | # Console Setup 3 | ### 4 | from rich.console import Console 5 | 6 | console = Console() 7 | console._log_render.omit_repeated_times = False -------------------------------------------------------------------------------- /palettesnap/outdatedCheck.py: -------------------------------------------------------------------------------- 1 | ### 2 | # Modules 3 | ### 4 | 5 | # External modules 6 | import subprocess 7 | import requests 8 | 9 | # Internal modules 10 | from .console import console 11 | 12 | ### 13 | # Main functions 14 | ### 15 | def findCurrVersion() -> str | None: 16 | '''returns user-installed version''' 17 | proc = subprocess.run(['pip', 'show', 'palettesnap'], capture_output=True) 18 | if proc.returncode != 0: 19 | # pip failed to find palettesnap 20 | return None 21 | res = proc.stdout.decode("utf-8") 22 | version = res.splitlines()[1] 23 | version = version.split(" ")[1] 24 | return version 25 | 26 | def findLatestVersion() -> str | None: 27 | '''returns latest version on PyPi''' 28 | url = "https://pypi.org/pypi/palettesnap/json" 29 | try: 30 | response = requests.get(url) 31 | response.raise_for_status() 32 | version = response.json()["info"]["version"] 33 | return version 34 | except requests.exceptions.HTTPError: 35 | return None 36 | 37 | def outdatedCheck() -> None: 38 | '''check if user-installed version is outdated or not''' 39 | current = findCurrVersion() 40 | latest = findLatestVersion() 41 | if latest is None or current is None: 42 | console.log("Failed to check for palettesnap updates.") 43 | elif current != latest: 44 | console.log("Your version of palettesnap is [red]outdated[/red].") 45 | console.log(f"Current version: {current}.") 46 | console.log(f"Latest version: {latest}.") 47 | else: 48 | console.log("Your version of palettesnap is [green]up-to-date[/green].") 49 | -------------------------------------------------------------------------------- /palettesnap/preview.py: -------------------------------------------------------------------------------- 1 | ### 2 | # Modules 3 | ### 4 | 5 | # External modules 6 | import os 7 | import subprocess 8 | import psutil 9 | import toml 10 | 11 | # Internal modules 12 | from . import setup 13 | from .console import console 14 | from .colorClass import hexToRgb 15 | 16 | ### 17 | # Terminal function 18 | ### 19 | def iTermCheck() -> bool: 20 | '''checks if terminal is iTerm2 terminal''' 21 | result = os.getenv("ITERM_SESSION_ID") 22 | return result != None 23 | 24 | def getTerminal() -> str: 25 | '''identify the terminal type''' 26 | # python script's ppid is terminal shell's pid 27 | # terminal type is shell's ppid 28 | # Get the shell's PPID 29 | shellPID = os.getppid() 30 | terminalPID = psutil.Process(shellPID).ppid() 31 | # Get the terminal type 32 | cmd = f"ps -p {terminalPID} | awk 'NR==2 {{print $NF}}'" 33 | result = subprocess.run(cmd, shell=True, capture_output=True, text=True).stdout 34 | result = result[:-1] 35 | # parse the result 36 | if "wezterm" in result or "WezTerm" in result: 37 | return "wezterm" 38 | elif "kitty" in result or "Kitty" in result: 39 | return "kitty" 40 | elif iTermCheck(): 41 | return "iterm" 42 | else: 43 | return "NA" 44 | 45 | ### 46 | # Image function 47 | ### 48 | 49 | def showImage(path : str, terminal : str) -> None: 50 | '''prints the image in the terminal''' 51 | if terminal == "wezterm": 52 | subprocess.run(["wezterm", "imgcat", path, "--height", "45%"]) 53 | elif terminal == "kitty": 54 | subprocess.run(["kitty", "+kitten", "icat", path]) 55 | elif terminal == "iterm": 56 | subprocess.run(["imgcat", path]) 57 | else: 58 | console.log("Terminal type not supported for displaying images.") 59 | console.log("Skipping set to display the image.") 60 | 61 | ### 62 | # Color function 63 | ### 64 | def readPalette() -> dict[str, str]: 65 | '''reads palette.toml and returns''' 66 | path = os.path.join(setup.cache, "palette.toml") 67 | return toml.load(path) 68 | 69 | def rgbToAnsi(fg : tuple[int, int, int], bg : tuple[int, int, int], text : str) -> str: 70 | '''returns text with fg and bg color''' 71 | fgRed, fgGreen, fgBlue = fg 72 | bgRed, bgGreen, bgBlue = bg 73 | res = f"\033[38;2;{fgRed};{fgGreen};{fgBlue}m\033[48;2;{bgRed};{bgGreen};{bgBlue}m" 74 | res = res + text + "\033[0m" 75 | return res 76 | 77 | def colorRow(label : str, hexColor : str, colorDict : dict[str, str]) -> str: 78 | '''creates a row for the colors''' 79 | maxLen = max([len(key) for key in colorDict]) 80 | text = "PaletteSnap"[:maxLen] 81 | row = label + " " * (maxLen - len(label)) + "|" 82 | fgColor = hexToRgb(hexColor) 83 | for key in colorDict: 84 | bgColor = hexToRgb(colorDict[key]) 85 | row = row + rgbToAnsi(fgColor, bgColor, text) + "|" 86 | row = row[:-1] + "\n" 87 | return row 88 | 89 | def printColors(colorDict : dict[str, str]) -> None: 90 | '''prints the colors in the terminal''' 91 | maxLen = max([len(key) for key in colorDict]) 92 | # first row 93 | firstRow = " " * maxLen + "|" 94 | for key in colorDict: 95 | firstRow = firstRow + key + " " * (maxLen - len(key)) + "|" 96 | firstRow = firstRow[:-1] + "\n" # remove last | 97 | finalRes = firstRow 98 | # do other rows 99 | for key in colorDict: 100 | row = colorRow(key, colorDict[key], colorDict) 101 | finalRes += row 102 | print(finalRes) 103 | return None 104 | 105 | ### 106 | # Main function 107 | ### 108 | def previewPalette(imageFlag) -> None: 109 | # Get image path and colors 110 | console.log("Terminal must support [blue]24-bit / true color[/blue] for previewing to work.") 111 | if imageFlag: 112 | palette = readPalette() 113 | imgPath = palette["image"] 114 | palette.pop("image") 115 | palette.pop("mode") 116 | # Get terminal type and print image 117 | terminal = getTerminal() 118 | showImage(imgPath, terminal) 119 | # Print colors 120 | printColors(palette) -------------------------------------------------------------------------------- /palettesnap/refresh.py: -------------------------------------------------------------------------------- 1 | ### 2 | # Modules 3 | ### 4 | # External modules 5 | import subprocess 6 | from os import chdir 7 | from shlex import split 8 | 9 | # Internal modules 10 | from .setup import home 11 | from .console import console 12 | 13 | ### 14 | # Helper Function 15 | ### 16 | def isProgramOpen(name): 17 | '''checks if program is currently open or not''' 18 | output = subprocess.run(f"pgrep -a {name}", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 19 | if output.returncode == 0: 20 | return True 21 | else: 22 | return False 23 | 24 | ### 25 | # Main Function 26 | ### 27 | def refreshProgram(program, cmd): 28 | '''refresh opened programs''' 29 | if isProgramOpen(program): 30 | console.log(f"Refreshing [u]{program}[/u]") 31 | cmdList = split(cmd) 32 | subprocess.run(cmdList) -------------------------------------------------------------------------------- /palettesnap/setup.py: -------------------------------------------------------------------------------- 1 | ### 2 | # Modules 3 | ### 4 | import os 5 | import platform 6 | import toml 7 | 8 | ### 9 | # Identify the OS 10 | ### 11 | osType = platform.system() 12 | 13 | ### 14 | # Define program directories 15 | ### 16 | # .config stores config files for PaletteSnap 17 | # .cache stores the output 18 | 19 | # Set up the environment 20 | home = os.getenv('HOME', os.getenv('USERPROFILE')) 21 | xdgCache = os.getenv('XDG_CACHE_HOME', os.path.join(home, '.cache')) 22 | xdgConfig = os.getenv('XDG_CONFIG_HOME', os.path.join(home, '.config')) 23 | 24 | # Define the directories 25 | cache = os.path.join(xdgCache, 'palsnap') 26 | config = os.path.join(xdgConfig, 'palsnap') 27 | #module = os.path.dirname(__file__) 28 | 29 | # Create the directories 30 | os.makedirs(config, exist_ok=True) 31 | os.makedirs(cache, exist_ok=True) 32 | 33 | # Create palsnap.toml 34 | defaultAccents = { 35 | "black": "#000000", 36 | "red": "#ff0000", 37 | "orange": "#ffa500", 38 | "yellow": "#ffff00", 39 | "green": "#008000", 40 | "blue": "#0000ff", 41 | "cyan": "#00ffff", 42 | "magenta": "#ff00ff", 43 | "violet": "#7f00ff", 44 | "white": "#ffffff" 45 | } 46 | palsnapFile = os.path.join(config, 'palsnap.toml') 47 | if not os.path.isfile(palsnapFile): 48 | # create the file 49 | with open(palsnapFile, 'w') as file: 50 | # default accent colors 51 | toml.dump(defaultAccents, file) 52 | file.close() 53 | 54 | # Create template.toml 55 | templateFile = os.path.join(config, 'templates.toml') 56 | if not os.path.isfile(templateFile): 57 | # create the file 58 | with open(templateFile, 'w') as file: 59 | pass 60 | file.close() 61 | 62 | # Create the template folder 63 | os.makedirs(os.path.join(config, 'templates'), exist_ok=True) 64 | templateDir = os.path.join(config, 'templates') -------------------------------------------------------------------------------- /palettesnap/templating.py: -------------------------------------------------------------------------------- 1 | ### 2 | # Modules 3 | ### 4 | 5 | # External modules 6 | import os 7 | import toml 8 | from string import Template 9 | from re import sub, findall 10 | 11 | # Internal modules 12 | from . import setup 13 | from .refresh import refreshProgram 14 | from .console import console 15 | from .colorClass import Color 16 | 17 | ### 18 | # PaletteSnap built-in export functions 19 | ### 20 | def exportHTML(): 21 | '''exports HTML template file for viewing''' 22 | path = os.path.join(setup.cache, "PaletteTest.html") 23 | htmlRes = ''' 24 | 25 | 26 | 27 | 28 | 35 | PaletteSnap Test 36 | 37 | 38 |

Test Terminal:

39 |
from PIL import ImageColor 40 | 41 | # Distance function 42 | def distance(p1, p2): 43 | x1, y1, z1 = p1 44 | x2, y2, z2 = p2 45 | return ((x2 - x1)**2 + (y2 - y1)**2 + (z2 - z1)**2)**2 46 | 47 | # Color class 48 | class Color: 49 | def __init__(self, hex): 50 | self.hex = hex 51 | self.hexDigits = self.hex[1:] 52 | self.rgb = ImageColor.getrgb(self.hex) 53 | 54 | def __repr__(self): 55 | return "Hex string: " + self.hex 56 |
57 | 58 | 59 | ''' 60 | # Overwrite file at path 61 | output = open(path, "w") 62 | output.write(htmlRes) 63 | output.close() 64 | 65 | 66 | def exportCSS(colorDict : dict[str, Color]) -> None: 67 | '''exports palette as CSS file''' 68 | path = os.path.join(setup.cache, "styles.css") 69 | cssTemplate = Template(''' 70 | div { 71 | color: $fg; 72 | background-color: $bg; 73 | border: 3px solid $fg; 74 | } 75 | ::-moz-selection { /* Code for Firefox */ 76 | background: $selection; 77 | } 78 | ::selection { 79 | background: $selection; 80 | } 81 | #comment { 82 | color: $comment !important; 83 | } 84 | #red { 85 | color: $red !important; 86 | } 87 | #green { 88 | color: $green !important; 89 | } 90 | #orange { 91 | color: $orange !important; 92 | } 93 | #magenta { 94 | color: $magenta !important; 95 | } 96 | #yellow { 97 | color: $yellow !important; 98 | } 99 | #blue { 100 | color: $blue !important; 101 | } 102 | #cyan { 103 | color: $cyan !important; 104 | } 105 | ''') 106 | # comment and selection color 107 | commentColor = colorDict["bg3"].hex 108 | selectionColor = colorDict["bg4"].hex 109 | 110 | cssRes = cssTemplate.substitute( 111 | fg = colorDict["fg"].hex, 112 | bg = colorDict["bg"].hex, 113 | comment = commentColor, 114 | selection = selectionColor, 115 | red = colorDict["red"].hex, 116 | green = colorDict["green"].hex, 117 | orange = colorDict["orange"].hex, 118 | magenta = colorDict["magenta"].hex, 119 | yellow = colorDict["yellow"].hex, 120 | blue = colorDict["blue"].hex, 121 | cyan = colorDict["cyan"].hex 122 | ) 123 | # Overwrite file at path 124 | output = open(path, "w") 125 | output.write(cssRes) 126 | output.close() 127 | 128 | ### 129 | # Template export functions 130 | ### 131 | def replaceVar(tempContents : str, palette : dict[str, Color]) -> str: 132 | '''replaces variable in template with colors in palette''' 133 | # replace vars with colors 134 | modePattern = r"\{\{mode \| (.*?) \| (.*?)\}\}" 135 | for key in palette: 136 | if key == "image" or key == "mode": 137 | # image/mode 138 | tempContents = tempContents.replace("{{" + key + "}}", palette[key]) 139 | # special case for image 140 | tempContents = tempContents.replace("{{image.name}}", os.path.basename(palette[key])) 141 | # special case for mode 142 | mode = palette["mode"] 143 | tempContents = sub(modePattern, lambda match: match.group(1) if mode == 'light' else match.group(2), tempContents) 144 | else: 145 | # hex 146 | tempContents = tempContents.replace("{{" + key + "}}", palette[key].hex) 147 | tempContents = tempContents.replace("{{" + key + ".digits}}", (palette[key].hex)[1:]) 148 | # rgb 149 | tempContents = tempContents.replace("{{" + key + ".r}}", str(palette[key].rgb[0])) 150 | tempContents = tempContents.replace("{{" + key + ".g}}", str(palette[key].rgb[1])) 151 | tempContents = tempContents.replace("{{" + key + ".b}}", str(palette[key].rgb[2])) 152 | # normalized rgb 153 | tempContents = tempContents.replace("{{" + key + ".nr}}", str(palette[key].normalRgb[0])) 154 | tempContents = tempContents.replace("{{" + key + ".ng}}", str(palette[key].normalRgb[1])) 155 | tempContents = tempContents.replace("{{" + key + ".nb}}", str(palette[key].normalRgb[2])) 156 | # hsl 157 | tempContents = tempContents.replace("{{" + key + ".h}}", str(palette[key].hsl[0])) 158 | tempContents = tempContents.replace("{{" + key + ".s}}", str(palette[key].hsl[1])) 159 | tempContents = tempContents.replace("{{" + key + ".l}}", str(palette[key].hsl[2])) 160 | # lighten, darken, and mode inverse 161 | lightenPattern = r"\{\{(.*?).lighten\(([0-9.\d]+)\)\}\}" 162 | darkenPattern = r"\{\{(.*?).darken\(([0-9.\d]+)\)\}\}" 163 | modeInversePattern = r"\{\{(.*?).modeInverse\(([0-9.\d]+)\)\}\}" 164 | lightenMatches = findall(lightenPattern, tempContents) 165 | darkenMatches = findall(darkenPattern, tempContents) 166 | modeInverseMatches = findall(modeInversePattern, tempContents) 167 | for match in lightenMatches: 168 | name = match[0] 169 | value = float(match[1]) 170 | if name in palette: 171 | currColor = palette[name] 172 | tempContents = tempContents.replace("{{" + name + ".lighten(" + str(value) + ")}}", Color.lighten(currColor, value).hex) 173 | else: 174 | console.log(f"Cannot lighten {name} with value {value}") 175 | for match in darkenMatches: 176 | name = match[0] 177 | value = float(match[1]) 178 | if name in palette: 179 | currColor = palette[name] 180 | tempContents = tempContents.replace("{{" + name + ".darken(" + str(value) + ")}}", Color.darken(currColor, value).hex) 181 | else: 182 | console.log(f"Cannot darken {name} with value {value}") 183 | for match in modeInverseMatches: 184 | mode = palette["mode"] 185 | name = match[0] 186 | value = float(match[1]) 187 | # mode inverse darkens a color if the mode is light 188 | # mode inverse lightens a color if the mode is dark 189 | if name in palette: 190 | currColor = palette[name] 191 | if mode == "light": 192 | newColor = Color.darken(currColor, value).hex 193 | else: 194 | newColor = Color.lighten(currColor, value).hex 195 | tempContents = tempContents.replace("{{" + name + ".modeInverse(" + str(value) + ")}}", newColor) 196 | else: 197 | console.log(f"Cannot apply mode inverse to {name} with value {value}") 198 | return tempContents 199 | 200 | def exportTemplates(palette : dict[str, Color]) -> None: 201 | '''export templates''' 202 | console.log("Generating templates.") 203 | templateInfo = toml.load(setup.templateFile) 204 | for program in templateInfo: 205 | programInfo : list[dict[str, str]] = templateInfo[program] 206 | for tempDict in programInfo: 207 | # extract information 208 | for key, value in tempDict.items(): 209 | if key == "name": 210 | name = value 211 | elif key == "alias": 212 | alias = value 213 | elif key == "dir": 214 | dir = os.path.expanduser(value) 215 | else: 216 | cmd = value 217 | # handle empty information 218 | if alias == "": 219 | alias = name 220 | if dir == "": 221 | dir = setup.cache 222 | # replace variables for template 223 | tempLoc = os.path.join(setup.templateDir, name) 224 | exportLoc = os.path.join(dir, alias) 225 | # check if template file exists 226 | if os.path.isfile(tempLoc): 227 | file = open(tempLoc, "r") 228 | template = file.read() 229 | file.close() 230 | # generate the file based on the template 231 | newContents = replaceVar(template, palette) 232 | with open(exportLoc, "w") as newFile: 233 | newFile.write(newContents) 234 | newFile.close() 235 | console.log(f"Template {name} succesfully generated.") 236 | else: 237 | console.log(f"Template {name} does not exist.") 238 | # refresh the program 239 | if cmd != "": 240 | refreshProgram(program, cmd) 241 | 242 | ### 243 | # Export all 244 | ### 245 | 246 | def exportAll(colors : dict[str, Color]) -> None: 247 | exportHTML() 248 | #exportCSS(colors) 249 | exportTemplates(colors) -------------------------------------------------------------------------------- /palettesnap/wallpaper.py: -------------------------------------------------------------------------------- 1 | ### 2 | # Modules 3 | ### 4 | 5 | # External modules 6 | import os 7 | import subprocess 8 | 9 | # Interal modules 10 | from . import setup 11 | from .console import console 12 | 13 | ### 14 | # Helper functions 15 | ### 16 | def untestWarn() -> None: 17 | '''prints the untested warning message to console''' 18 | console.log("You are using one of the untested OS or Linux environments.") 19 | console.log("Please let the creator know if it works or not.") 20 | return None 21 | 22 | def findEnv(): 23 | '''return the desktop environment''' 24 | env = os.getenv('XDG_CURRENT_DESKTOP', '') 25 | if "GNOME" in env: 26 | return "GNOME" 27 | elif "KDE" in env: 28 | return "KDE" 29 | elif "XFCE" in env: 30 | return "XFCE" 31 | elif "MATE" in env: 32 | return "MATE" 33 | else: 34 | return None 35 | 36 | def setMacWallpaper(imgPath): 37 | '''set the wallpaper for macOS''' 38 | subprocess.run(['osascript', '-e', 'tell application \"System Events\" to tell every desktop to set picture to \"%s\" as POSIX file' % imgPath]) 39 | 40 | # based off pywal's https://raw.githubusercontent.com/dylanaraps/pywal/master/pywal/wallpaper.py 41 | def setLinuxWallpaper(env, imgPath): 42 | '''set the wallpaper for Linux''' 43 | if env == "GNOME": 44 | untestWarn() 45 | subprocess.run(['gsettings', 'set', 'org.gnome.desktop.background', 'picture-uri', f'file://{imgPath}']) 46 | elif env == "KDE": 47 | kdeCommand = [ 48 | "qdbus", 49 | "org.kde.plasmashell", 50 | "/PlasmaShell", 51 | "org.kde.PlasmaShell.evaluateScript", 52 | f'var allDesktops = desktops();print (allDesktops);for (i=0;i=7.2)", "sphinx-copybutton"] 214 | mypy = ["contourpy[bokeh,docs]", "docutils-stubs", "mypy (==1.8.0)", "types-Pillow"] 215 | test = ["Pillow", "contourpy[test-no-images]", "matplotlib"] 216 | test-no-images = ["pytest", "pytest-cov", "pytest-xdist", "wurlitzer"] 217 | 218 | [[package]] 219 | name = "cycler" 220 | version = "0.12.1" 221 | description = "Composable style cycles" 222 | optional = false 223 | python-versions = ">=3.8" 224 | files = [ 225 | {file = "cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30"}, 226 | {file = "cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c"}, 227 | ] 228 | 229 | [package.extras] 230 | docs = ["ipython", "matplotlib", "numpydoc", "sphinx"] 231 | tests = ["pytest", "pytest-cov", "pytest-xdist"] 232 | 233 | [[package]] 234 | name = "fonttools" 235 | version = "4.53.1" 236 | description = "Tools to manipulate font files" 237 | optional = false 238 | python-versions = ">=3.8" 239 | files = [ 240 | {file = "fonttools-4.53.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0679a30b59d74b6242909945429dbddb08496935b82f91ea9bf6ad240ec23397"}, 241 | {file = "fonttools-4.53.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e8bf06b94694251861ba7fdeea15c8ec0967f84c3d4143ae9daf42bbc7717fe3"}, 242 | {file = "fonttools-4.53.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b96cd370a61f4d083c9c0053bf634279b094308d52fdc2dd9a22d8372fdd590d"}, 243 | {file = "fonttools-4.53.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a1c7c5aa18dd3b17995898b4a9b5929d69ef6ae2af5b96d585ff4005033d82f0"}, 244 | {file = "fonttools-4.53.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e013aae589c1c12505da64a7d8d023e584987e51e62006e1bb30d72f26522c41"}, 245 | {file = "fonttools-4.53.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:9efd176f874cb6402e607e4cc9b4a9cd584d82fc34a4b0c811970b32ba62501f"}, 246 | {file = "fonttools-4.53.1-cp310-cp310-win32.whl", hash = "sha256:c8696544c964500aa9439efb6761947393b70b17ef4e82d73277413f291260a4"}, 247 | {file = "fonttools-4.53.1-cp310-cp310-win_amd64.whl", hash = "sha256:8959a59de5af6d2bec27489e98ef25a397cfa1774b375d5787509c06659b3671"}, 248 | {file = "fonttools-4.53.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:da33440b1413bad53a8674393c5d29ce64d8c1a15ef8a77c642ffd900d07bfe1"}, 249 | {file = "fonttools-4.53.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5ff7e5e9bad94e3a70c5cd2fa27f20b9bb9385e10cddab567b85ce5d306ea923"}, 250 | {file = "fonttools-4.53.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c6e7170d675d12eac12ad1a981d90f118c06cf680b42a2d74c6c931e54b50719"}, 251 | {file = "fonttools-4.53.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bee32ea8765e859670c4447b0817514ca79054463b6b79784b08a8df3a4d78e3"}, 252 | {file = "fonttools-4.53.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6e08f572625a1ee682115223eabebc4c6a2035a6917eac6f60350aba297ccadb"}, 253 | {file = "fonttools-4.53.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b21952c092ffd827504de7e66b62aba26fdb5f9d1e435c52477e6486e9d128b2"}, 254 | {file = "fonttools-4.53.1-cp311-cp311-win32.whl", hash = "sha256:9dfdae43b7996af46ff9da520998a32b105c7f098aeea06b2226b30e74fbba88"}, 255 | {file = "fonttools-4.53.1-cp311-cp311-win_amd64.whl", hash = "sha256:d4d0096cb1ac7a77b3b41cd78c9b6bc4a400550e21dc7a92f2b5ab53ed74eb02"}, 256 | {file = "fonttools-4.53.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:d92d3c2a1b39631a6131c2fa25b5406855f97969b068e7e08413325bc0afba58"}, 257 | {file = "fonttools-4.53.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3b3c8ebafbee8d9002bd8f1195d09ed2bd9ff134ddec37ee8f6a6375e6a4f0e8"}, 258 | {file = "fonttools-4.53.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:32f029c095ad66c425b0ee85553d0dc326d45d7059dbc227330fc29b43e8ba60"}, 259 | {file = "fonttools-4.53.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10f5e6c3510b79ea27bb1ebfcc67048cde9ec67afa87c7dd7efa5c700491ac7f"}, 260 | {file = "fonttools-4.53.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f677ce218976496a587ab17140da141557beb91d2a5c1a14212c994093f2eae2"}, 261 | {file = "fonttools-4.53.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9e6ceba2a01b448e36754983d376064730690401da1dd104ddb543519470a15f"}, 262 | {file = "fonttools-4.53.1-cp312-cp312-win32.whl", hash = "sha256:791b31ebbc05197d7aa096bbc7bd76d591f05905d2fd908bf103af4488e60670"}, 263 | {file = "fonttools-4.53.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ed170b5e17da0264b9f6fae86073be3db15fa1bd74061c8331022bca6d09bab"}, 264 | {file = "fonttools-4.53.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:c818c058404eb2bba05e728d38049438afd649e3c409796723dfc17cd3f08749"}, 265 | {file = "fonttools-4.53.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:651390c3b26b0c7d1f4407cad281ee7a5a85a31a110cbac5269de72a51551ba2"}, 266 | {file = "fonttools-4.53.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e54f1bba2f655924c1138bbc7fa91abd61f45c68bd65ab5ed985942712864bbb"}, 267 | {file = "fonttools-4.53.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9cd19cf4fe0595ebdd1d4915882b9440c3a6d30b008f3cc7587c1da7b95be5f"}, 268 | {file = "fonttools-4.53.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:2af40ae9cdcb204fc1d8f26b190aa16534fcd4f0df756268df674a270eab575d"}, 269 | {file = "fonttools-4.53.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:35250099b0cfb32d799fb5d6c651220a642fe2e3c7d2560490e6f1d3f9ae9169"}, 270 | {file = "fonttools-4.53.1-cp38-cp38-win32.whl", hash = "sha256:f08df60fbd8d289152079a65da4e66a447efc1d5d5a4d3f299cdd39e3b2e4a7d"}, 271 | {file = "fonttools-4.53.1-cp38-cp38-win_amd64.whl", hash = "sha256:7b6b35e52ddc8fb0db562133894e6ef5b4e54e1283dff606fda3eed938c36fc8"}, 272 | {file = "fonttools-4.53.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:75a157d8d26c06e64ace9df037ee93a4938a4606a38cb7ffaf6635e60e253b7a"}, 273 | {file = "fonttools-4.53.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4824c198f714ab5559c5be10fd1adf876712aa7989882a4ec887bf1ef3e00e31"}, 274 | {file = "fonttools-4.53.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:becc5d7cb89c7b7afa8321b6bb3dbee0eec2b57855c90b3e9bf5fb816671fa7c"}, 275 | {file = "fonttools-4.53.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:84ec3fb43befb54be490147b4a922b5314e16372a643004f182babee9f9c3407"}, 276 | {file = "fonttools-4.53.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:73379d3ffdeecb376640cd8ed03e9d2d0e568c9d1a4e9b16504a834ebadc2dfb"}, 277 | {file = "fonttools-4.53.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:02569e9a810f9d11f4ae82c391ebc6fb5730d95a0657d24d754ed7763fb2d122"}, 278 | {file = "fonttools-4.53.1-cp39-cp39-win32.whl", hash = "sha256:aae7bd54187e8bf7fd69f8ab87b2885253d3575163ad4d669a262fe97f0136cb"}, 279 | {file = "fonttools-4.53.1-cp39-cp39-win_amd64.whl", hash = "sha256:e5b708073ea3d684235648786f5f6153a48dc8762cdfe5563c57e80787c29fbb"}, 280 | {file = "fonttools-4.53.1-py3-none-any.whl", hash = "sha256:f1f8758a2ad110bd6432203a344269f445a2907dc24ef6bccfd0ac4e14e0d71d"}, 281 | {file = "fonttools-4.53.1.tar.gz", hash = "sha256:e128778a8e9bc11159ce5447f76766cefbd876f44bd79aff030287254e4752c4"}, 282 | ] 283 | 284 | [package.extras] 285 | all = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "fs (>=2.2.0,<3)", "lxml (>=4.0)", "lz4 (>=1.7.4.2)", "matplotlib", "munkres", "pycairo", "scipy", "skia-pathops (>=0.5.0)", "sympy", "uharfbuzz (>=0.23.0)", "unicodedata2 (>=15.1.0)", "xattr", "zopfli (>=0.1.4)"] 286 | graphite = ["lz4 (>=1.7.4.2)"] 287 | interpolatable = ["munkres", "pycairo", "scipy"] 288 | lxml = ["lxml (>=4.0)"] 289 | pathops = ["skia-pathops (>=0.5.0)"] 290 | plot = ["matplotlib"] 291 | repacker = ["uharfbuzz (>=0.23.0)"] 292 | symfont = ["sympy"] 293 | type1 = ["xattr"] 294 | ufo = ["fs (>=2.2.0,<3)"] 295 | unicode = ["unicodedata2 (>=15.1.0)"] 296 | woff = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "zopfli (>=0.1.4)"] 297 | 298 | [[package]] 299 | name = "idna" 300 | version = "3.7" 301 | description = "Internationalized Domain Names in Applications (IDNA)" 302 | optional = false 303 | python-versions = ">=3.5" 304 | files = [ 305 | {file = "idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"}, 306 | {file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"}, 307 | ] 308 | 309 | [[package]] 310 | name = "imageio" 311 | version = "2.34.2" 312 | description = "Library for reading and writing a wide range of image, video, scientific, and volumetric data formats." 313 | optional = false 314 | python-versions = ">=3.8" 315 | files = [ 316 | {file = "imageio-2.34.2-py3-none-any.whl", hash = "sha256:a0bb27ec9d5bab36a9f4835e51b21d2cb099e1f78451441f94687ff3404b79f8"}, 317 | {file = "imageio-2.34.2.tar.gz", hash = "sha256:5c0c0ee8faa018a1c42f649b90395dd4d3bb6187c09053a0cd6f1fdd51bbff5e"}, 318 | ] 319 | 320 | [package.dependencies] 321 | numpy = "*" 322 | pillow = ">=8.3.2" 323 | 324 | [package.extras] 325 | all-plugins = ["astropy", "av", "imageio-ffmpeg", "pillow-heif", "psutil", "tifffile"] 326 | all-plugins-pypy = ["av", "imageio-ffmpeg", "pillow-heif", "psutil", "tifffile"] 327 | build = ["wheel"] 328 | dev = ["black", "flake8", "fsspec[github]", "pytest", "pytest-cov"] 329 | docs = ["numpydoc", "pydata-sphinx-theme", "sphinx (<6)"] 330 | ffmpeg = ["imageio-ffmpeg", "psutil"] 331 | fits = ["astropy"] 332 | full = ["astropy", "av", "black", "flake8", "fsspec[github]", "gdal", "imageio-ffmpeg", "itk", "numpydoc", "pillow-heif", "psutil", "pydata-sphinx-theme", "pytest", "pytest-cov", "sphinx (<6)", "tifffile", "wheel"] 333 | gdal = ["gdal"] 334 | itk = ["itk"] 335 | linting = ["black", "flake8"] 336 | pillow-heif = ["pillow-heif"] 337 | pyav = ["av"] 338 | test = ["fsspec[github]", "pytest", "pytest-cov"] 339 | tifffile = ["tifffile"] 340 | 341 | [[package]] 342 | name = "joblib" 343 | version = "1.4.2" 344 | description = "Lightweight pipelining with Python functions" 345 | optional = false 346 | python-versions = ">=3.8" 347 | files = [ 348 | {file = "joblib-1.4.2-py3-none-any.whl", hash = "sha256:06d478d5674cbc267e7496a410ee875abd68e4340feff4490bcb7afb88060ae6"}, 349 | {file = "joblib-1.4.2.tar.gz", hash = "sha256:2382c5816b2636fbd20a09e0f4e9dad4736765fdfb7dca582943b9c1366b3f0e"}, 350 | ] 351 | 352 | [[package]] 353 | name = "kiwisolver" 354 | version = "1.4.5" 355 | description = "A fast implementation of the Cassowary constraint solver" 356 | optional = false 357 | python-versions = ">=3.7" 358 | files = [ 359 | {file = "kiwisolver-1.4.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:05703cf211d585109fcd72207a31bb170a0f22144d68298dc5e61b3c946518af"}, 360 | {file = "kiwisolver-1.4.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:146d14bebb7f1dc4d5fbf74f8a6cb15ac42baadee8912eb84ac0b3b2a3dc6ac3"}, 361 | {file = "kiwisolver-1.4.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6ef7afcd2d281494c0a9101d5c571970708ad911d028137cd558f02b851c08b4"}, 362 | {file = "kiwisolver-1.4.5-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:9eaa8b117dc8337728e834b9c6e2611f10c79e38f65157c4c38e9400286f5cb1"}, 363 | {file = "kiwisolver-1.4.5-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ec20916e7b4cbfb1f12380e46486ec4bcbaa91a9c448b97023fde0d5bbf9e4ff"}, 364 | {file = "kiwisolver-1.4.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:39b42c68602539407884cf70d6a480a469b93b81b7701378ba5e2328660c847a"}, 365 | {file = "kiwisolver-1.4.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aa12042de0171fad672b6c59df69106d20d5596e4f87b5e8f76df757a7c399aa"}, 366 | {file = "kiwisolver-1.4.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2a40773c71d7ccdd3798f6489aaac9eee213d566850a9533f8d26332d626b82c"}, 367 | {file = "kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:19df6e621f6d8b4b9c4d45f40a66839294ff2bb235e64d2178f7522d9170ac5b"}, 368 | {file = "kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:83d78376d0d4fd884e2c114d0621624b73d2aba4e2788182d286309ebdeed770"}, 369 | {file = "kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:e391b1f0a8a5a10ab3b9bb6afcfd74f2175f24f8975fb87ecae700d1503cdee0"}, 370 | {file = "kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:852542f9481f4a62dbb5dd99e8ab7aedfeb8fb6342349a181d4036877410f525"}, 371 | {file = "kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59edc41b24031bc25108e210c0def6f6c2191210492a972d585a06ff246bb79b"}, 372 | {file = "kiwisolver-1.4.5-cp310-cp310-win32.whl", hash = "sha256:a6aa6315319a052b4ee378aa171959c898a6183f15c1e541821c5c59beaa0238"}, 373 | {file = "kiwisolver-1.4.5-cp310-cp310-win_amd64.whl", hash = "sha256:d0ef46024e6a3d79c01ff13801cb19d0cad7fd859b15037aec74315540acc276"}, 374 | {file = "kiwisolver-1.4.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:11863aa14a51fd6ec28688d76f1735f8f69ab1fabf388851a595d0721af042f5"}, 375 | {file = "kiwisolver-1.4.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8ab3919a9997ab7ef2fbbed0cc99bb28d3c13e6d4b1ad36e97e482558a91be90"}, 376 | {file = "kiwisolver-1.4.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fcc700eadbbccbf6bc1bcb9dbe0786b4b1cb91ca0dcda336eef5c2beed37b797"}, 377 | {file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dfdd7c0b105af050eb3d64997809dc21da247cf44e63dc73ff0fd20b96be55a9"}, 378 | {file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76c6a5964640638cdeaa0c359382e5703e9293030fe730018ca06bc2010c4437"}, 379 | {file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bbea0db94288e29afcc4c28afbf3a7ccaf2d7e027489c449cf7e8f83c6346eb9"}, 380 | {file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ceec1a6bc6cab1d6ff5d06592a91a692f90ec7505d6463a88a52cc0eb58545da"}, 381 | {file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:040c1aebeda72197ef477a906782b5ab0d387642e93bda547336b8957c61022e"}, 382 | {file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f91de7223d4c7b793867797bacd1ee53bfe7359bd70d27b7b58a04efbb9436c8"}, 383 | {file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:faae4860798c31530dd184046a900e652c95513796ef51a12bc086710c2eec4d"}, 384 | {file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:b0157420efcb803e71d1b28e2c287518b8808b7cf1ab8af36718fd0a2c453eb0"}, 385 | {file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:06f54715b7737c2fecdbf140d1afb11a33d59508a47bf11bb38ecf21dc9ab79f"}, 386 | {file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fdb7adb641a0d13bdcd4ef48e062363d8a9ad4a182ac7647ec88f695e719ae9f"}, 387 | {file = "kiwisolver-1.4.5-cp311-cp311-win32.whl", hash = "sha256:bb86433b1cfe686da83ce32a9d3a8dd308e85c76b60896d58f082136f10bffac"}, 388 | {file = "kiwisolver-1.4.5-cp311-cp311-win_amd64.whl", hash = "sha256:6c08e1312a9cf1074d17b17728d3dfce2a5125b2d791527f33ffbe805200a355"}, 389 | {file = "kiwisolver-1.4.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:32d5cf40c4f7c7b3ca500f8985eb3fb3a7dfc023215e876f207956b5ea26632a"}, 390 | {file = "kiwisolver-1.4.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f846c260f483d1fd217fe5ed7c173fb109efa6b1fc8381c8b7552c5781756192"}, 391 | {file = "kiwisolver-1.4.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5ff5cf3571589b6d13bfbfd6bcd7a3f659e42f96b5fd1c4830c4cf21d4f5ef45"}, 392 | {file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7269d9e5f1084a653d575c7ec012ff57f0c042258bf5db0954bf551c158466e7"}, 393 | {file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da802a19d6e15dffe4b0c24b38b3af68e6c1a68e6e1d8f30148c83864f3881db"}, 394 | {file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3aba7311af82e335dd1e36ffff68aaca609ca6290c2cb6d821a39aa075d8e3ff"}, 395 | {file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:763773d53f07244148ccac5b084da5adb90bfaee39c197554f01b286cf869228"}, 396 | {file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2270953c0d8cdab5d422bee7d2007f043473f9d2999631c86a223c9db56cbd16"}, 397 | {file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d099e745a512f7e3bbe7249ca835f4d357c586d78d79ae8f1dcd4d8adeb9bda9"}, 398 | {file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:74db36e14a7d1ce0986fa104f7d5637aea5c82ca6326ed0ec5694280942d1162"}, 399 | {file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:7e5bab140c309cb3a6ce373a9e71eb7e4873c70c2dda01df6820474f9889d6d4"}, 400 | {file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:0f114aa76dc1b8f636d077979c0ac22e7cd8f3493abbab152f20eb8d3cda71f3"}, 401 | {file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:88a2df29d4724b9237fc0c6eaf2a1adae0cdc0b3e9f4d8e7dc54b16812d2d81a"}, 402 | {file = "kiwisolver-1.4.5-cp312-cp312-win32.whl", hash = "sha256:72d40b33e834371fd330fb1472ca19d9b8327acb79a5821d4008391db8e29f20"}, 403 | {file = "kiwisolver-1.4.5-cp312-cp312-win_amd64.whl", hash = "sha256:2c5674c4e74d939b9d91dda0fae10597ac7521768fec9e399c70a1f27e2ea2d9"}, 404 | {file = "kiwisolver-1.4.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:3a2b053a0ab7a3960c98725cfb0bf5b48ba82f64ec95fe06f1d06c99b552e130"}, 405 | {file = "kiwisolver-1.4.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3cd32d6c13807e5c66a7cbb79f90b553642f296ae4518a60d8d76243b0ad2898"}, 406 | {file = "kiwisolver-1.4.5-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:59ec7b7c7e1a61061850d53aaf8e93db63dce0c936db1fda2658b70e4a1be709"}, 407 | {file = "kiwisolver-1.4.5-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:da4cfb373035def307905d05041c1d06d8936452fe89d464743ae7fb8371078b"}, 408 | {file = "kiwisolver-1.4.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2400873bccc260b6ae184b2b8a4fec0e4082d30648eadb7c3d9a13405d861e89"}, 409 | {file = "kiwisolver-1.4.5-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:1b04139c4236a0f3aff534479b58f6f849a8b351e1314826c2d230849ed48985"}, 410 | {file = "kiwisolver-1.4.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:4e66e81a5779b65ac21764c295087de82235597a2293d18d943f8e9e32746265"}, 411 | {file = "kiwisolver-1.4.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:7931d8f1f67c4be9ba1dd9c451fb0eeca1a25b89e4d3f89e828fe12a519b782a"}, 412 | {file = "kiwisolver-1.4.5-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:b3f7e75f3015df442238cca659f8baa5f42ce2a8582727981cbfa15fee0ee205"}, 413 | {file = "kiwisolver-1.4.5-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:bbf1d63eef84b2e8c89011b7f2235b1e0bf7dacc11cac9431fc6468e99ac77fb"}, 414 | {file = "kiwisolver-1.4.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:4c380469bd3f970ef677bf2bcba2b6b0b4d5c75e7a020fb863ef75084efad66f"}, 415 | {file = "kiwisolver-1.4.5-cp37-cp37m-win32.whl", hash = "sha256:9408acf3270c4b6baad483865191e3e582b638b1654a007c62e3efe96f09a9a3"}, 416 | {file = "kiwisolver-1.4.5-cp37-cp37m-win_amd64.whl", hash = "sha256:5b94529f9b2591b7af5f3e0e730a4e0a41ea174af35a4fd067775f9bdfeee01a"}, 417 | {file = "kiwisolver-1.4.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:11c7de8f692fc99816e8ac50d1d1aef4f75126eefc33ac79aac02c099fd3db71"}, 418 | {file = "kiwisolver-1.4.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:53abb58632235cd154176ced1ae8f0d29a6657aa1aa9decf50b899b755bc2b93"}, 419 | {file = "kiwisolver-1.4.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:88b9f257ca61b838b6f8094a62418421f87ac2a1069f7e896c36a7d86b5d4c29"}, 420 | {file = "kiwisolver-1.4.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3195782b26fc03aa9c6913d5bad5aeb864bdc372924c093b0f1cebad603dd712"}, 421 | {file = "kiwisolver-1.4.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fc579bf0f502e54926519451b920e875f433aceb4624a3646b3252b5caa9e0b6"}, 422 | {file = "kiwisolver-1.4.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5a580c91d686376f0f7c295357595c5a026e6cbc3d77b7c36e290201e7c11ecb"}, 423 | {file = "kiwisolver-1.4.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cfe6ab8da05c01ba6fbea630377b5da2cd9bcbc6338510116b01c1bc939a2c18"}, 424 | {file = "kiwisolver-1.4.5-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:d2e5a98f0ec99beb3c10e13b387f8db39106d53993f498b295f0c914328b1333"}, 425 | {file = "kiwisolver-1.4.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a51a263952b1429e429ff236d2f5a21c5125437861baeed77f5e1cc2d2c7c6da"}, 426 | {file = "kiwisolver-1.4.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:3edd2fa14e68c9be82c5b16689e8d63d89fe927e56debd6e1dbce7a26a17f81b"}, 427 | {file = "kiwisolver-1.4.5-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:74d1b44c6cfc897df648cc9fdaa09bc3e7679926e6f96df05775d4fb3946571c"}, 428 | {file = "kiwisolver-1.4.5-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:76d9289ed3f7501012e05abb8358bbb129149dbd173f1f57a1bf1c22d19ab7cc"}, 429 | {file = "kiwisolver-1.4.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:92dea1ffe3714fa8eb6a314d2b3c773208d865a0e0d35e713ec54eea08a66250"}, 430 | {file = "kiwisolver-1.4.5-cp38-cp38-win32.whl", hash = "sha256:5c90ae8c8d32e472be041e76f9d2f2dbff4d0b0be8bd4041770eddb18cf49a4e"}, 431 | {file = "kiwisolver-1.4.5-cp38-cp38-win_amd64.whl", hash = "sha256:c7940c1dc63eb37a67721b10d703247552416f719c4188c54e04334321351ced"}, 432 | {file = "kiwisolver-1.4.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:9407b6a5f0d675e8a827ad8742e1d6b49d9c1a1da5d952a67d50ef5f4170b18d"}, 433 | {file = "kiwisolver-1.4.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:15568384086b6df3c65353820a4473575dbad192e35010f622c6ce3eebd57af9"}, 434 | {file = "kiwisolver-1.4.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0dc9db8e79f0036e8173c466d21ef18e1befc02de8bf8aa8dc0813a6dc8a7046"}, 435 | {file = "kiwisolver-1.4.5-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:cdc8a402aaee9a798b50d8b827d7ecf75edc5fb35ea0f91f213ff927c15f4ff0"}, 436 | {file = "kiwisolver-1.4.5-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6c3bd3cde54cafb87d74d8db50b909705c62b17c2099b8f2e25b461882e544ff"}, 437 | {file = "kiwisolver-1.4.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:955e8513d07a283056b1396e9a57ceddbd272d9252c14f154d450d227606eb54"}, 438 | {file = "kiwisolver-1.4.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:346f5343b9e3f00b8db8ba359350eb124b98c99efd0b408728ac6ebf38173958"}, 439 | {file = "kiwisolver-1.4.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b9098e0049e88c6a24ff64545cdfc50807818ba6c1b739cae221bbbcbc58aad3"}, 440 | {file = "kiwisolver-1.4.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:00bd361b903dc4bbf4eb165f24d1acbee754fce22ded24c3d56eec268658a5cf"}, 441 | {file = "kiwisolver-1.4.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7b8b454bac16428b22560d0a1cf0a09875339cab69df61d7805bf48919415901"}, 442 | {file = "kiwisolver-1.4.5-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:f1d072c2eb0ad60d4c183f3fb44ac6f73fb7a8f16a2694a91f988275cbf352f9"}, 443 | {file = "kiwisolver-1.4.5-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:31a82d498054cac9f6d0b53d02bb85811185bcb477d4b60144f915f3b3126342"}, 444 | {file = "kiwisolver-1.4.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6512cb89e334e4700febbffaaa52761b65b4f5a3cf33f960213d5656cea36a77"}, 445 | {file = "kiwisolver-1.4.5-cp39-cp39-win32.whl", hash = "sha256:9db8ea4c388fdb0f780fe91346fd438657ea602d58348753d9fb265ce1bca67f"}, 446 | {file = "kiwisolver-1.4.5-cp39-cp39-win_amd64.whl", hash = "sha256:59415f46a37f7f2efeec758353dd2eae1b07640d8ca0f0c42548ec4125492635"}, 447 | {file = "kiwisolver-1.4.5-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:5c7b3b3a728dc6faf3fc372ef24f21d1e3cee2ac3e9596691d746e5a536de920"}, 448 | {file = "kiwisolver-1.4.5-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:620ced262a86244e2be10a676b646f29c34537d0d9cc8eb26c08f53d98013390"}, 449 | {file = "kiwisolver-1.4.5-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:378a214a1e3bbf5ac4a8708304318b4f890da88c9e6a07699c4ae7174c09a68d"}, 450 | {file = "kiwisolver-1.4.5-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aaf7be1207676ac608a50cd08f102f6742dbfc70e8d60c4db1c6897f62f71523"}, 451 | {file = "kiwisolver-1.4.5-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:ba55dce0a9b8ff59495ddd050a0225d58bd0983d09f87cfe2b6aec4f2c1234e4"}, 452 | {file = "kiwisolver-1.4.5-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:fd32ea360bcbb92d28933fc05ed09bffcb1704ba3fc7942e81db0fd4f81a7892"}, 453 | {file = "kiwisolver-1.4.5-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5e7139af55d1688f8b960ee9ad5adafc4ac17c1c473fe07133ac092310d76544"}, 454 | {file = "kiwisolver-1.4.5-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:dced8146011d2bc2e883f9bd68618b8247387f4bbec46d7392b3c3b032640126"}, 455 | {file = "kiwisolver-1.4.5-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c9bf3325c47b11b2e51bca0824ea217c7cd84491d8ac4eefd1e409705ef092bd"}, 456 | {file = "kiwisolver-1.4.5-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:5794cf59533bc3f1b1c821f7206a3617999db9fbefc345360aafe2e067514929"}, 457 | {file = "kiwisolver-1.4.5-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:e368f200bbc2e4f905b8e71eb38b3c04333bddaa6a2464a6355487b02bb7fb09"}, 458 | {file = "kiwisolver-1.4.5-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e5d706eba36b4c4d5bc6c6377bb6568098765e990cfc21ee16d13963fab7b3e7"}, 459 | {file = "kiwisolver-1.4.5-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85267bd1aa8880a9c88a8cb71e18d3d64d2751a790e6ca6c27b8ccc724bcd5ad"}, 460 | {file = "kiwisolver-1.4.5-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:210ef2c3a1f03272649aff1ef992df2e724748918c4bc2d5a90352849eb40bea"}, 461 | {file = "kiwisolver-1.4.5-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:11d011a7574eb3b82bcc9c1a1d35c1d7075677fdd15de527d91b46bd35e935ee"}, 462 | {file = "kiwisolver-1.4.5.tar.gz", hash = "sha256:e57e563a57fb22a142da34f38acc2fc1a5c864bc29ca1517a88abc963e60d6ec"}, 463 | ] 464 | 465 | [[package]] 466 | name = "kmedoids" 467 | version = "0.5.1" 468 | description = "k-Medoids Clustering in Python with FasterPAM" 469 | optional = false 470 | python-versions = "*" 471 | files = [ 472 | {file = "kmedoids-0.5.1-cp310-cp310-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:2a5c6bac64d82adf29d4aa4989938789895eb8a1fee1c51d696db17a6d81f944"}, 473 | {file = "kmedoids-0.5.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:731a2b2ef2ee2ee169b299a341d1cc678da31e31567d3120712f2cf9a5b3361b"}, 474 | {file = "kmedoids-0.5.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4c39559ab88d7fdc205a20bf99ad973cc0700dea33679ba1283b1d290babbfd7"}, 475 | {file = "kmedoids-0.5.1-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:c589080866b61680712563d92998ac54bf5fd9bffe5f37d311cb490ca1fdba8b"}, 476 | {file = "kmedoids-0.5.1-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:453ba9336bb0f30be65b8e8a9a421d8cff0feadc1751880be18d66bba835dd46"}, 477 | {file = "kmedoids-0.5.1-cp310-none-win_amd64.whl", hash = "sha256:4215466abbafae42e107b115562acbd9ace8b760e8f347ef59316dc9e7b3a6e2"}, 478 | {file = "kmedoids-0.5.1-cp311-cp311-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:78d5bf0d6f8d72c402f5f4df643a4bba0df5ad3f915028408db97523ea771cdf"}, 479 | {file = "kmedoids-0.5.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:761ea521dd1d90ca64c6a63fa9d2ff88fe83b7ac85c82a76b52795ca8d20441d"}, 480 | {file = "kmedoids-0.5.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:763e5af82194127f7e84c2753b9beccc1bcc7962b37cca9ba0ece777759a6658"}, 481 | {file = "kmedoids-0.5.1-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:3b1297600886d50512a7d923633820698ef2d68d10b985a70ae32ea5e7cd8c37"}, 482 | {file = "kmedoids-0.5.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:91eb445825c7f59b338f227b88cc52c4ee1589a821b31c21c1f37f8723a154da"}, 483 | {file = "kmedoids-0.5.1-cp311-none-win_amd64.whl", hash = "sha256:4935cbc691396bb5b6e9eb905d298a33f027fa6e81ab9fe7735bf5c56531b14b"}, 484 | {file = "kmedoids-0.5.1-cp312-cp312-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:105aff65add39542524d121bf66b48909bef5dd06fbe7b6209f697eea1820058"}, 485 | {file = "kmedoids-0.5.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:eba433df1639bbf8cdf73c5c284811d118576f341d37e35982d68d4e7ff94cf1"}, 486 | {file = "kmedoids-0.5.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b775c73055543d88915286f9833cb579ac2a19c6e4f766539457350b682ce779"}, 487 | {file = "kmedoids-0.5.1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:3d1e5137421c5c4f1e9cbeb50653fa2411db33d59a3edc3c54b6f136b7071ef4"}, 488 | {file = "kmedoids-0.5.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:2949ddf69cd1ed610ac411cd1a2351c86f66bcda343aa956343fcf75efa95dd1"}, 489 | {file = "kmedoids-0.5.1-cp312-none-win_amd64.whl", hash = "sha256:fe96d9f072537670f370d7eb2352e98c23a2d65374889701e375b612e549a4ac"}, 490 | {file = "kmedoids-0.5.1-cp38-cp38-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:0e52eb9f5b6ed48c8972cfb75f59bf0f64045208ab86fec549e18cca9c675f0c"}, 491 | {file = "kmedoids-0.5.1-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:bae9f473cce6952add4ec8596ce3c559d20156c54a62af2a5a32967d20019667"}, 492 | {file = "kmedoids-0.5.1-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f9b66be7a5dcca6c49815bf450424e1aa4a012abafdb28f84def5c882a0f241c"}, 493 | {file = "kmedoids-0.5.1-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:a64e7a64edd25d62400e882a9276ea0a144d86f2b5662b3af312ca779507d94c"}, 494 | {file = "kmedoids-0.5.1-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:eb98d48bb68c0683ed6a22ddac84db988199ce517652a0c12e1c83400f21ef04"}, 495 | {file = "kmedoids-0.5.1-cp38-none-win_amd64.whl", hash = "sha256:5d233e7b354f1e7e293b413ff65a248c16dbd58a67e4c9bd8d23117ea682ebf5"}, 496 | {file = "kmedoids-0.5.1-cp39-cp39-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:6b39d64e3938e8f6dcca8ac1c76f71b0884c62a4eaaee86519cf1a63c0a46763"}, 497 | {file = "kmedoids-0.5.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:802be8fd10cee053f74a612cd5146d80f02e8d4d683c889db5f8e6d6fcc32774"}, 498 | {file = "kmedoids-0.5.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1b4cfbc10b71a98f64fb17a0932c42ef866b14c702b4f9f0552512ce027afe6e"}, 499 | {file = "kmedoids-0.5.1-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:f6774988d2ca249ec8a0c9539ebbedd7f32e853b6b20fd390d22e3741792ac59"}, 500 | {file = "kmedoids-0.5.1-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:ff94bf47e9948ff6b34144e00bd91286af9cc2c78092f2adefa24a016b42fdbc"}, 501 | {file = "kmedoids-0.5.1-cp39-none-win_amd64.whl", hash = "sha256:bcf25bb9b04c63cc21df36514e726892301c2ab5c2ae8a0fd93be5034b751567"}, 502 | ] 503 | 504 | [[package]] 505 | name = "markdown-it-py" 506 | version = "3.0.0" 507 | description = "Python port of markdown-it. Markdown parsing, done right!" 508 | optional = false 509 | python-versions = ">=3.8" 510 | files = [ 511 | {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, 512 | {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, 513 | ] 514 | 515 | [package.dependencies] 516 | mdurl = ">=0.1,<1.0" 517 | 518 | [package.extras] 519 | benchmarking = ["psutil", "pytest", "pytest-benchmark"] 520 | code-style = ["pre-commit (>=3.0,<4.0)"] 521 | compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"] 522 | linkify = ["linkify-it-py (>=1,<3)"] 523 | plugins = ["mdit-py-plugins"] 524 | profiling = ["gprof2dot"] 525 | rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"] 526 | testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] 527 | 528 | [[package]] 529 | name = "matplotlib" 530 | version = "3.9.0" 531 | description = "Python plotting package" 532 | optional = false 533 | python-versions = ">=3.9" 534 | files = [ 535 | {file = "matplotlib-3.9.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2bcee1dffaf60fe7656183ac2190bd630842ff87b3153afb3e384d966b57fe56"}, 536 | {file = "matplotlib-3.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3f988bafb0fa39d1074ddd5bacd958c853e11def40800c5824556eb630f94d3b"}, 537 | {file = "matplotlib-3.9.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fe428e191ea016bb278758c8ee82a8129c51d81d8c4bc0846c09e7e8e9057241"}, 538 | {file = "matplotlib-3.9.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eaf3978060a106fab40c328778b148f590e27f6fa3cd15a19d6892575bce387d"}, 539 | {file = "matplotlib-3.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2e7f03e5cbbfacdd48c8ea394d365d91ee8f3cae7e6ec611409927b5ed997ee4"}, 540 | {file = "matplotlib-3.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:13beb4840317d45ffd4183a778685e215939be7b08616f431c7795276e067463"}, 541 | {file = "matplotlib-3.9.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:063af8587fceeac13b0936c42a2b6c732c2ab1c98d38abc3337e430e1ff75e38"}, 542 | {file = "matplotlib-3.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9a2fa6d899e17ddca6d6526cf6e7ba677738bf2a6a9590d702c277204a7c6152"}, 543 | {file = "matplotlib-3.9.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:550cdda3adbd596078cca7d13ed50b77879104e2e46392dcd7c75259d8f00e85"}, 544 | {file = "matplotlib-3.9.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76cce0f31b351e3551d1f3779420cf8f6ec0d4a8cf9c0237a3b549fd28eb4abb"}, 545 | {file = "matplotlib-3.9.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c53aeb514ccbbcbab55a27f912d79ea30ab21ee0531ee2c09f13800efb272674"}, 546 | {file = "matplotlib-3.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:a5be985db2596d761cdf0c2eaf52396f26e6a64ab46bd8cd810c48972349d1be"}, 547 | {file = "matplotlib-3.9.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:c79f3a585f1368da6049318bdf1f85568d8d04b2e89fc24b7e02cc9b62017382"}, 548 | {file = "matplotlib-3.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bdd1ecbe268eb3e7653e04f451635f0fb0f77f07fd070242b44c076c9106da84"}, 549 | {file = "matplotlib-3.9.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d38e85a1a6d732f645f1403ce5e6727fd9418cd4574521d5803d3d94911038e5"}, 550 | {file = "matplotlib-3.9.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0a490715b3b9984fa609116481b22178348c1a220a4499cda79132000a79b4db"}, 551 | {file = "matplotlib-3.9.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8146ce83cbc5dc71c223a74a1996d446cd35cfb6a04b683e1446b7e6c73603b7"}, 552 | {file = "matplotlib-3.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:d91a4ffc587bacf5c4ce4ecfe4bcd23a4b675e76315f2866e588686cc97fccdf"}, 553 | {file = "matplotlib-3.9.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:616fabf4981a3b3c5a15cd95eba359c8489c4e20e03717aea42866d8d0465956"}, 554 | {file = "matplotlib-3.9.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cd53c79fd02f1c1808d2cfc87dd3cf4dbc63c5244a58ee7944497107469c8d8a"}, 555 | {file = "matplotlib-3.9.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:06a478f0d67636554fa78558cfbcd7b9dba85b51f5c3b5a0c9be49010cf5f321"}, 556 | {file = "matplotlib-3.9.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81c40af649d19c85f8073e25e5806926986806fa6d54be506fbf02aef47d5a89"}, 557 | {file = "matplotlib-3.9.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:52146fc3bd7813cc784562cb93a15788be0b2875c4655e2cc6ea646bfa30344b"}, 558 | {file = "matplotlib-3.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:0fc51eaa5262553868461c083d9adadb11a6017315f3a757fc45ec6ec5f02888"}, 559 | {file = "matplotlib-3.9.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:bd4f2831168afac55b881db82a7730992aa41c4f007f1913465fb182d6fb20c0"}, 560 | {file = "matplotlib-3.9.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:290d304e59be2b33ef5c2d768d0237f5bd132986bdcc66f80bc9bcc300066a03"}, 561 | {file = "matplotlib-3.9.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ff2e239c26be4f24bfa45860c20ffccd118d270c5b5d081fa4ea409b5469fcd"}, 562 | {file = "matplotlib-3.9.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:af4001b7cae70f7eaacfb063db605280058246de590fa7874f00f62259f2df7e"}, 563 | {file = "matplotlib-3.9.0.tar.gz", hash = "sha256:e6d29ea6c19e34b30fb7d88b7081f869a03014f66fe06d62cc77d5a6ea88ed7a"}, 564 | ] 565 | 566 | [package.dependencies] 567 | contourpy = ">=1.0.1" 568 | cycler = ">=0.10" 569 | fonttools = ">=4.22.0" 570 | kiwisolver = ">=1.3.1" 571 | numpy = ">=1.23" 572 | packaging = ">=20.0" 573 | pillow = ">=8" 574 | pyparsing = ">=2.3.1" 575 | python-dateutil = ">=2.7" 576 | 577 | [package.extras] 578 | dev = ["meson-python (>=0.13.1)", "numpy (>=1.25)", "pybind11 (>=2.6)", "setuptools (>=64)", "setuptools_scm (>=7)"] 579 | 580 | [[package]] 581 | name = "mdurl" 582 | version = "0.1.2" 583 | description = "Markdown URL utilities" 584 | optional = false 585 | python-versions = ">=3.7" 586 | files = [ 587 | {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, 588 | {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, 589 | ] 590 | 591 | [[package]] 592 | name = "numpy" 593 | version = "1.26.4" 594 | description = "Fundamental package for array computing in Python" 595 | optional = false 596 | python-versions = ">=3.9" 597 | files = [ 598 | {file = "numpy-1.26.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9ff0f4f29c51e2803569d7a51c2304de5554655a60c5d776e35b4a41413830d0"}, 599 | {file = "numpy-1.26.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e4ee3380d6de9c9ec04745830fd9e2eccb3e6cf790d39d7b98ffd19b0dd754a"}, 600 | {file = "numpy-1.26.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d209d8969599b27ad20994c8e41936ee0964e6da07478d6c35016bc386b66ad4"}, 601 | {file = "numpy-1.26.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ffa75af20b44f8dba823498024771d5ac50620e6915abac414251bd971b4529f"}, 602 | {file = "numpy-1.26.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:62b8e4b1e28009ef2846b4c7852046736bab361f7aeadeb6a5b89ebec3c7055a"}, 603 | {file = "numpy-1.26.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a4abb4f9001ad2858e7ac189089c42178fcce737e4169dc61321660f1a96c7d2"}, 604 | {file = "numpy-1.26.4-cp310-cp310-win32.whl", hash = "sha256:bfe25acf8b437eb2a8b2d49d443800a5f18508cd811fea3181723922a8a82b07"}, 605 | {file = "numpy-1.26.4-cp310-cp310-win_amd64.whl", hash = "sha256:b97fe8060236edf3662adfc2c633f56a08ae30560c56310562cb4f95500022d5"}, 606 | {file = "numpy-1.26.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c66707fabe114439db9068ee468c26bbdf909cac0fb58686a42a24de1760c71"}, 607 | {file = "numpy-1.26.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:edd8b5fe47dab091176d21bb6de568acdd906d1887a4584a15a9a96a1dca06ef"}, 608 | {file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ab55401287bfec946ced39700c053796e7cc0e3acbef09993a9ad2adba6ca6e"}, 609 | {file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:666dbfb6ec68962c033a450943ded891bed2d54e6755e35e5835d63f4f6931d5"}, 610 | {file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:96ff0b2ad353d8f990b63294c8986f1ec3cb19d749234014f4e7eb0112ceba5a"}, 611 | {file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:60dedbb91afcbfdc9bc0b1f3f402804070deed7392c23eb7a7f07fa857868e8a"}, 612 | {file = "numpy-1.26.4-cp311-cp311-win32.whl", hash = "sha256:1af303d6b2210eb850fcf03064d364652b7120803a0b872f5211f5234b399f20"}, 613 | {file = "numpy-1.26.4-cp311-cp311-win_amd64.whl", hash = "sha256:cd25bcecc4974d09257ffcd1f098ee778f7834c3ad767fe5db785be9a4aa9cb2"}, 614 | {file = "numpy-1.26.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b3ce300f3644fb06443ee2222c2201dd3a89ea6040541412b8fa189341847218"}, 615 | {file = "numpy-1.26.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:03a8c78d01d9781b28a6989f6fa1bb2c4f2d51201cf99d3dd875df6fbd96b23b"}, 616 | {file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9fad7dcb1aac3c7f0584a5a8133e3a43eeb2fe127f47e3632d43d677c66c102b"}, 617 | {file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:675d61ffbfa78604709862923189bad94014bef562cc35cf61d3a07bba02a7ed"}, 618 | {file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ab47dbe5cc8210f55aa58e4805fe224dac469cde56b9f731a4c098b91917159a"}, 619 | {file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1dda2e7b4ec9dd512f84935c5f126c8bd8b9f2fc001e9f54af255e8c5f16b0e0"}, 620 | {file = "numpy-1.26.4-cp312-cp312-win32.whl", hash = "sha256:50193e430acfc1346175fcbdaa28ffec49947a06918b7b92130744e81e640110"}, 621 | {file = "numpy-1.26.4-cp312-cp312-win_amd64.whl", hash = "sha256:08beddf13648eb95f8d867350f6a018a4be2e5ad54c8d8caed89ebca558b2818"}, 622 | {file = "numpy-1.26.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7349ab0fa0c429c82442a27a9673fc802ffdb7c7775fad780226cb234965e53c"}, 623 | {file = "numpy-1.26.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:52b8b60467cd7dd1e9ed082188b4e6bb35aa5cdd01777621a1658910745b90be"}, 624 | {file = "numpy-1.26.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5241e0a80d808d70546c697135da2c613f30e28251ff8307eb72ba696945764"}, 625 | {file = "numpy-1.26.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f870204a840a60da0b12273ef34f7051e98c3b5961b61b0c2c1be6dfd64fbcd3"}, 626 | {file = "numpy-1.26.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:679b0076f67ecc0138fd2ede3a8fd196dddc2ad3254069bcb9faf9a79b1cebcd"}, 627 | {file = "numpy-1.26.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:47711010ad8555514b434df65f7d7b076bb8261df1ca9bb78f53d3b2db02e95c"}, 628 | {file = "numpy-1.26.4-cp39-cp39-win32.whl", hash = "sha256:a354325ee03388678242a4d7ebcd08b5c727033fcff3b2f536aea978e15ee9e6"}, 629 | {file = "numpy-1.26.4-cp39-cp39-win_amd64.whl", hash = "sha256:3373d5d70a5fe74a2c1bb6d2cfd9609ecf686d47a2d7b1d37a8f3b6bf6003aea"}, 630 | {file = "numpy-1.26.4-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:afedb719a9dcfc7eaf2287b839d8198e06dcd4cb5d276a3df279231138e83d30"}, 631 | {file = "numpy-1.26.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95a7476c59002f2f6c590b9b7b998306fba6a5aa646b1e22ddfeaf8f78c3a29c"}, 632 | {file = "numpy-1.26.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7e50d0a0cc3189f9cb0aeb3a6a6af18c16f59f004b866cd2be1c14b36134a4a0"}, 633 | {file = "numpy-1.26.4.tar.gz", hash = "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010"}, 634 | ] 635 | 636 | [[package]] 637 | name = "packaging" 638 | version = "24.1" 639 | description = "Core utilities for Python packages" 640 | optional = false 641 | python-versions = ">=3.8" 642 | files = [ 643 | {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, 644 | {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, 645 | ] 646 | 647 | [[package]] 648 | name = "pillow" 649 | version = "10.4.0" 650 | description = "Python Imaging Library (Fork)" 651 | optional = false 652 | python-versions = ">=3.8" 653 | files = [ 654 | {file = "pillow-10.4.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:4d9667937cfa347525b319ae34375c37b9ee6b525440f3ef48542fcf66f2731e"}, 655 | {file = "pillow-10.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:543f3dc61c18dafb755773efc89aae60d06b6596a63914107f75459cf984164d"}, 656 | {file = "pillow-10.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7928ecbf1ece13956b95d9cbcfc77137652b02763ba384d9ab508099a2eca856"}, 657 | {file = "pillow-10.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4d49b85c4348ea0b31ea63bc75a9f3857869174e2bf17e7aba02945cd218e6f"}, 658 | {file = "pillow-10.4.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:6c762a5b0997f5659a5ef2266abc1d8851ad7749ad9a6a5506eb23d314e4f46b"}, 659 | {file = "pillow-10.4.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:a985e028fc183bf12a77a8bbf36318db4238a3ded7fa9df1b9a133f1cb79f8fc"}, 660 | {file = "pillow-10.4.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:812f7342b0eee081eaec84d91423d1b4650bb9828eb53d8511bcef8ce5aecf1e"}, 661 | {file = "pillow-10.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ac1452d2fbe4978c2eec89fb5a23b8387aba707ac72810d9490118817d9c0b46"}, 662 | {file = "pillow-10.4.0-cp310-cp310-win32.whl", hash = "sha256:bcd5e41a859bf2e84fdc42f4edb7d9aba0a13d29a2abadccafad99de3feff984"}, 663 | {file = "pillow-10.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:ecd85a8d3e79cd7158dec1c9e5808e821feea088e2f69a974db5edf84dc53141"}, 664 | {file = "pillow-10.4.0-cp310-cp310-win_arm64.whl", hash = "sha256:ff337c552345e95702c5fde3158acb0625111017d0e5f24bf3acdb9cc16b90d1"}, 665 | {file = "pillow-10.4.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:0a9ec697746f268507404647e531e92889890a087e03681a3606d9b920fbee3c"}, 666 | {file = "pillow-10.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dfe91cb65544a1321e631e696759491ae04a2ea11d36715eca01ce07284738be"}, 667 | {file = "pillow-10.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5dc6761a6efc781e6a1544206f22c80c3af4c8cf461206d46a1e6006e4429ff3"}, 668 | {file = "pillow-10.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e84b6cc6a4a3d76c153a6b19270b3526a5a8ed6b09501d3af891daa2a9de7d6"}, 669 | {file = "pillow-10.4.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:bbc527b519bd3aa9d7f429d152fea69f9ad37c95f0b02aebddff592688998abe"}, 670 | {file = "pillow-10.4.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:76a911dfe51a36041f2e756b00f96ed84677cdeb75d25c767f296c1c1eda1319"}, 671 | {file = "pillow-10.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:59291fb29317122398786c2d44427bbd1a6d7ff54017075b22be9d21aa59bd8d"}, 672 | {file = "pillow-10.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:416d3a5d0e8cfe4f27f574362435bc9bae57f679a7158e0096ad2beb427b8696"}, 673 | {file = "pillow-10.4.0-cp311-cp311-win32.whl", hash = "sha256:7086cc1d5eebb91ad24ded9f58bec6c688e9f0ed7eb3dbbf1e4800280a896496"}, 674 | {file = "pillow-10.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:cbed61494057c0f83b83eb3a310f0bf774b09513307c434d4366ed64f4128a91"}, 675 | {file = "pillow-10.4.0-cp311-cp311-win_arm64.whl", hash = "sha256:f5f0c3e969c8f12dd2bb7e0b15d5c468b51e5017e01e2e867335c81903046a22"}, 676 | {file = "pillow-10.4.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:673655af3eadf4df6b5457033f086e90299fdd7a47983a13827acf7459c15d94"}, 677 | {file = "pillow-10.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:866b6942a92f56300012f5fbac71f2d610312ee65e22f1aa2609e491284e5597"}, 678 | {file = "pillow-10.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29dbdc4207642ea6aad70fbde1a9338753d33fb23ed6956e706936706f52dd80"}, 679 | {file = "pillow-10.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf2342ac639c4cf38799a44950bbc2dfcb685f052b9e262f446482afaf4bffca"}, 680 | {file = "pillow-10.4.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:f5b92f4d70791b4a67157321c4e8225d60b119c5cc9aee8ecf153aace4aad4ef"}, 681 | {file = "pillow-10.4.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:86dcb5a1eb778d8b25659d5e4341269e8590ad6b4e8b44d9f4b07f8d136c414a"}, 682 | {file = "pillow-10.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:780c072c2e11c9b2c7ca37f9a2ee8ba66f44367ac3e5c7832afcfe5104fd6d1b"}, 683 | {file = "pillow-10.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:37fb69d905be665f68f28a8bba3c6d3223c8efe1edf14cc4cfa06c241f8c81d9"}, 684 | {file = "pillow-10.4.0-cp312-cp312-win32.whl", hash = "sha256:7dfecdbad5c301d7b5bde160150b4db4c659cee2b69589705b6f8a0c509d9f42"}, 685 | {file = "pillow-10.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:1d846aea995ad352d4bdcc847535bd56e0fd88d36829d2c90be880ef1ee4668a"}, 686 | {file = "pillow-10.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:e553cad5179a66ba15bb18b353a19020e73a7921296a7979c4a2b7f6a5cd57f9"}, 687 | {file = "pillow-10.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8bc1a764ed8c957a2e9cacf97c8b2b053b70307cf2996aafd70e91a082e70df3"}, 688 | {file = "pillow-10.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6209bb41dc692ddfee4942517c19ee81b86c864b626dbfca272ec0f7cff5d9fb"}, 689 | {file = "pillow-10.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bee197b30783295d2eb680b311af15a20a8b24024a19c3a26431ff83eb8d1f70"}, 690 | {file = "pillow-10.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ef61f5dd14c300786318482456481463b9d6b91ebe5ef12f405afbba77ed0be"}, 691 | {file = "pillow-10.4.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:297e388da6e248c98bc4a02e018966af0c5f92dfacf5a5ca22fa01cb3179bca0"}, 692 | {file = "pillow-10.4.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:e4db64794ccdf6cb83a59d73405f63adbe2a1887012e308828596100a0b2f6cc"}, 693 | {file = "pillow-10.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bd2880a07482090a3bcb01f4265f1936a903d70bc740bfcb1fd4e8a2ffe5cf5a"}, 694 | {file = "pillow-10.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4b35b21b819ac1dbd1233317adeecd63495f6babf21b7b2512d244ff6c6ce309"}, 695 | {file = "pillow-10.4.0-cp313-cp313-win32.whl", hash = "sha256:551d3fd6e9dc15e4c1eb6fc4ba2b39c0c7933fa113b220057a34f4bb3268a060"}, 696 | {file = "pillow-10.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:030abdbe43ee02e0de642aee345efa443740aa4d828bfe8e2eb11922ea6a21ea"}, 697 | {file = "pillow-10.4.0-cp313-cp313-win_arm64.whl", hash = "sha256:5b001114dd152cfd6b23befeb28d7aee43553e2402c9f159807bf55f33af8a8d"}, 698 | {file = "pillow-10.4.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:8d4d5063501b6dd4024b8ac2f04962d661222d120381272deea52e3fc52d3736"}, 699 | {file = "pillow-10.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7c1ee6f42250df403c5f103cbd2768a28fe1a0ea1f0f03fe151c8741e1469c8b"}, 700 | {file = "pillow-10.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b15e02e9bb4c21e39876698abf233c8c579127986f8207200bc8a8f6bb27acf2"}, 701 | {file = "pillow-10.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a8d4bade9952ea9a77d0c3e49cbd8b2890a399422258a77f357b9cc9be8d680"}, 702 | {file = "pillow-10.4.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:43efea75eb06b95d1631cb784aa40156177bf9dd5b4b03ff38979e048258bc6b"}, 703 | {file = "pillow-10.4.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:950be4d8ba92aca4b2bb0741285a46bfae3ca699ef913ec8416c1b78eadd64cd"}, 704 | {file = "pillow-10.4.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:d7480af14364494365e89d6fddc510a13e5a2c3584cb19ef65415ca57252fb84"}, 705 | {file = "pillow-10.4.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:73664fe514b34c8f02452ffb73b7a92c6774e39a647087f83d67f010eb9a0cf0"}, 706 | {file = "pillow-10.4.0-cp38-cp38-win32.whl", hash = "sha256:e88d5e6ad0d026fba7bdab8c3f225a69f063f116462c49892b0149e21b6c0a0e"}, 707 | {file = "pillow-10.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:5161eef006d335e46895297f642341111945e2c1c899eb406882a6c61a4357ab"}, 708 | {file = "pillow-10.4.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:0ae24a547e8b711ccaaf99c9ae3cd975470e1a30caa80a6aaee9a2f19c05701d"}, 709 | {file = "pillow-10.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:298478fe4f77a4408895605f3482b6cc6222c018b2ce565c2b6b9c354ac3229b"}, 710 | {file = "pillow-10.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:134ace6dc392116566980ee7436477d844520a26a4b1bd4053f6f47d096997fd"}, 711 | {file = "pillow-10.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:930044bb7679ab003b14023138b50181899da3f25de50e9dbee23b61b4de2126"}, 712 | {file = "pillow-10.4.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:c76e5786951e72ed3686e122d14c5d7012f16c8303a674d18cdcd6d89557fc5b"}, 713 | {file = "pillow-10.4.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:b2724fdb354a868ddf9a880cb84d102da914e99119211ef7ecbdc613b8c96b3c"}, 714 | {file = "pillow-10.4.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:dbc6ae66518ab3c5847659e9988c3b60dc94ffb48ef9168656e0019a93dbf8a1"}, 715 | {file = "pillow-10.4.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:06b2f7898047ae93fad74467ec3d28fe84f7831370e3c258afa533f81ef7f3df"}, 716 | {file = "pillow-10.4.0-cp39-cp39-win32.whl", hash = "sha256:7970285ab628a3779aecc35823296a7869f889b8329c16ad5a71e4901a3dc4ef"}, 717 | {file = "pillow-10.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:961a7293b2457b405967af9c77dcaa43cc1a8cd50d23c532e62d48ab6cdd56f5"}, 718 | {file = "pillow-10.4.0-cp39-cp39-win_arm64.whl", hash = "sha256:32cda9e3d601a52baccb2856b8ea1fc213c90b340c542dcef77140dfa3278a9e"}, 719 | {file = "pillow-10.4.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:5b4815f2e65b30f5fbae9dfffa8636d992d49705723fe86a3661806e069352d4"}, 720 | {file = "pillow-10.4.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:8f0aef4ef59694b12cadee839e2ba6afeab89c0f39a3adc02ed51d109117b8da"}, 721 | {file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9f4727572e2918acaa9077c919cbbeb73bd2b3ebcfe033b72f858fc9fbef0026"}, 722 | {file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff25afb18123cea58a591ea0244b92eb1e61a1fd497bf6d6384f09bc3262ec3e"}, 723 | {file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:dc3e2db6ba09ffd7d02ae9141cfa0ae23393ee7687248d46a7507b75d610f4f5"}, 724 | {file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:02a2be69f9c9b8c1e97cf2713e789d4e398c751ecfd9967c18d0ce304efbf885"}, 725 | {file = "pillow-10.4.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:0755ffd4a0c6f267cccbae2e9903d95477ca2f77c4fcf3a3a09570001856c8a5"}, 726 | {file = "pillow-10.4.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:a02364621fe369e06200d4a16558e056fe2805d3468350df3aef21e00d26214b"}, 727 | {file = "pillow-10.4.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:1b5dea9831a90e9d0721ec417a80d4cbd7022093ac38a568db2dd78363b00908"}, 728 | {file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b885f89040bb8c4a1573566bbb2f44f5c505ef6e74cec7ab9068c900047f04b"}, 729 | {file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87dd88ded2e6d74d31e1e0a99a726a6765cda32d00ba72dc37f0651f306daaa8"}, 730 | {file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:2db98790afc70118bd0255c2eeb465e9767ecf1f3c25f9a1abb8ffc8cfd1fe0a"}, 731 | {file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:f7baece4ce06bade126fb84b8af1c33439a76d8a6fd818970215e0560ca28c27"}, 732 | {file = "pillow-10.4.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:cfdd747216947628af7b259d274771d84db2268ca062dd5faf373639d00113a3"}, 733 | {file = "pillow-10.4.0.tar.gz", hash = "sha256:166c1cd4d24309b30d61f79f4a9114b7b2313d7450912277855ff5dfd7cd4a06"}, 734 | ] 735 | 736 | [package.extras] 737 | docs = ["furo", "olefile", "sphinx (>=7.3)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinxext-opengraph"] 738 | fpx = ["olefile"] 739 | mic = ["olefile"] 740 | tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"] 741 | typing = ["typing-extensions"] 742 | xmp = ["defusedxml"] 743 | 744 | [[package]] 745 | name = "psutil" 746 | version = "6.0.0" 747 | description = "Cross-platform lib for process and system monitoring in Python." 748 | optional = false 749 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" 750 | files = [ 751 | {file = "psutil-6.0.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a021da3e881cd935e64a3d0a20983bda0bb4cf80e4f74fa9bfcb1bc5785360c6"}, 752 | {file = "psutil-6.0.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:1287c2b95f1c0a364d23bc6f2ea2365a8d4d9b726a3be7294296ff7ba97c17f0"}, 753 | {file = "psutil-6.0.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:a9a3dbfb4de4f18174528d87cc352d1f788b7496991cca33c6996f40c9e3c92c"}, 754 | {file = "psutil-6.0.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:6ec7588fb3ddaec7344a825afe298db83fe01bfaaab39155fa84cf1c0d6b13c3"}, 755 | {file = "psutil-6.0.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:1e7c870afcb7d91fdea2b37c24aeb08f98b6d67257a5cb0a8bc3ac68d0f1a68c"}, 756 | {file = "psutil-6.0.0-cp27-none-win32.whl", hash = "sha256:02b69001f44cc73c1c5279d02b30a817e339ceb258ad75997325e0e6169d8b35"}, 757 | {file = "psutil-6.0.0-cp27-none-win_amd64.whl", hash = "sha256:21f1fb635deccd510f69f485b87433460a603919b45e2a324ad65b0cc74f8fb1"}, 758 | {file = "psutil-6.0.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:c588a7e9b1173b6e866756dde596fd4cad94f9399daf99ad8c3258b3cb2b47a0"}, 759 | {file = "psutil-6.0.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ed2440ada7ef7d0d608f20ad89a04ec47d2d3ab7190896cd62ca5fc4fe08bf0"}, 760 | {file = "psutil-6.0.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5fd9a97c8e94059b0ef54a7d4baf13b405011176c3b6ff257c247cae0d560ecd"}, 761 | {file = "psutil-6.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2e8d0054fc88153ca0544f5c4d554d42e33df2e009c4ff42284ac9ebdef4132"}, 762 | {file = "psutil-6.0.0-cp36-cp36m-win32.whl", hash = "sha256:fc8c9510cde0146432bbdb433322861ee8c3efbf8589865c8bf8d21cb30c4d14"}, 763 | {file = "psutil-6.0.0-cp36-cp36m-win_amd64.whl", hash = "sha256:34859b8d8f423b86e4385ff3665d3f4d94be3cdf48221fbe476e883514fdb71c"}, 764 | {file = "psutil-6.0.0-cp37-abi3-win32.whl", hash = "sha256:a495580d6bae27291324fe60cea0b5a7c23fa36a7cd35035a16d93bdcf076b9d"}, 765 | {file = "psutil-6.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:33ea5e1c975250a720b3a6609c490db40dae5d83a4eb315170c4fe0d8b1f34b3"}, 766 | {file = "psutil-6.0.0-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:ffe7fc9b6b36beadc8c322f84e1caff51e8703b88eee1da46d1e3a6ae11b4fd0"}, 767 | {file = "psutil-6.0.0.tar.gz", hash = "sha256:8faae4f310b6d969fa26ca0545338b21f73c6b15db7c4a8d934a5482faa818f2"}, 768 | ] 769 | 770 | [package.extras] 771 | test = ["enum34", "ipaddress", "mock", "pywin32", "wmi"] 772 | 773 | [[package]] 774 | name = "pygments" 775 | version = "2.18.0" 776 | description = "Pygments is a syntax highlighting package written in Python." 777 | optional = false 778 | python-versions = ">=3.8" 779 | files = [ 780 | {file = "pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a"}, 781 | {file = "pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199"}, 782 | ] 783 | 784 | [package.extras] 785 | windows-terminal = ["colorama (>=0.4.6)"] 786 | 787 | [[package]] 788 | name = "pykdtree" 789 | version = "1.3.12" 790 | description = "Fast kd-tree implementation with OpenMP-enabled queries" 791 | optional = false 792 | python-versions = ">=3.9" 793 | files = [ 794 | {file = "pykdtree-1.3.12-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a072b845cc2983eb79fbfa9baeda4f4b3c3e6db0a54e1c89434d353246e66104"}, 795 | {file = "pykdtree-1.3.12-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:76d7f9ba21864701ac1206038abaf7613f35e96312731df770327e763ebb8d3a"}, 796 | {file = "pykdtree-1.3.12-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:431194955fd4f2be298a778643e36392bda9c43101dcb719146a050f3455d8cc"}, 797 | {file = "pykdtree-1.3.12-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b7437e854b976c51fb1781392d148d6d9244945e1504e49bf490370464848260"}, 798 | {file = "pykdtree-1.3.12-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:046efd4607208d6edeedee3d67754c60118086c6ca6a83c33ee01ec70749ea51"}, 799 | {file = "pykdtree-1.3.12-cp310-cp310-win_amd64.whl", hash = "sha256:e3ef09d033e4af683b08cde01a4f3c96d4a96329aa6e3135095325befac28007"}, 800 | {file = "pykdtree-1.3.12-cp310-cp310-win_arm64.whl", hash = "sha256:3ebab60fa3dab12791fd04987aef53315b8acae196f45e6bad58a2086c1b3436"}, 801 | {file = "pykdtree-1.3.12-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:83263e154871934423850d3b6eaaba6eb38463806d76313cbfa8798ecd7ca0a3"}, 802 | {file = "pykdtree-1.3.12-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b8079f357d2358d4c20b2cb779d36add251626ebab1aaf2e03ce4e70355eff49"}, 803 | {file = "pykdtree-1.3.12-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7771f1151e97080c3efb38e444ed6912dede203ca7e1d415f9b43f880cf92bcb"}, 804 | {file = "pykdtree-1.3.12-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7882706b092b501dd4e74d7658d9ab06d1eac2f88e819877a52cd7441b21a806"}, 805 | {file = "pykdtree-1.3.12-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d5f64e2dcc603c436ec7fd00c975f38537930cf1097e6ae4d9a80e1e900de528"}, 806 | {file = "pykdtree-1.3.12-cp311-cp311-win_amd64.whl", hash = "sha256:1b9473cc5c62f1a7e61f3cabe5d4098b9ec61a63040e1bbd74e5d4482c3603d9"}, 807 | {file = "pykdtree-1.3.12-cp311-cp311-win_arm64.whl", hash = "sha256:199585ff5a41c5f383bef3a68cff5c651b342045a8c7409576e3b17fd8a57f4e"}, 808 | {file = "pykdtree-1.3.12-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:1d20a8f54d29d255fad4bac26e80231067d9331bb9aacc2ee6743e4325b346fa"}, 809 | {file = "pykdtree-1.3.12-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f985d1b742452ff597fe9c9a0ea2c89cd85b298bcf3f6a565fa0d7ce66eb313f"}, 810 | {file = "pykdtree-1.3.12-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41020443270ef8aee5caf513d8a060126743a0aee19d33d35a6684fa2d37c13a"}, 811 | {file = "pykdtree-1.3.12-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88f52541f094ada031cee6ab44864d25d41ede97dbdb78cf55f5e9919e366841"}, 812 | {file = "pykdtree-1.3.12-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3fd826f886bff82b4c25cdc61df5142e309a45421388de2922bd315995d7fe8f"}, 813 | {file = "pykdtree-1.3.12-cp312-cp312-win_amd64.whl", hash = "sha256:c79d6db009714c1b2092366c78c5ffc645cfe5b7103ed4f94eabba3833b70088"}, 814 | {file = "pykdtree-1.3.12-cp312-cp312-win_arm64.whl", hash = "sha256:67a87b1a4f9ea484a3269cb7f468e3993bc03bc984ce63215302280061e5b691"}, 815 | {file = "pykdtree-1.3.12-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8c72357382c37e8d9af7e6b0257340a4c37bc20c9034559af11527034ba3e7b2"}, 816 | {file = "pykdtree-1.3.12-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9ba445012613a5284bfb57e119019138b15b363d39246a618d2e9994505baa2e"}, 817 | {file = "pykdtree-1.3.12-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8dfe19bab0e7bf92e69657d5c03b3fabc89c2afed64ae394700d09e00b86b07f"}, 818 | {file = "pykdtree-1.3.12-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba7d8dbd5976a029b59d5c9893fe0448f9c452ca3b08aaf9be4d8262fbeab42f"}, 819 | {file = "pykdtree-1.3.12-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:1e171cc813c54ff4cffce844230575334c7298aea0c78c1b33a533995608fce5"}, 820 | {file = "pykdtree-1.3.12-cp39-cp39-win_amd64.whl", hash = "sha256:b6cc13f4cc1a5ec967d47db088b6423a629127c43184e5857c631ccc64a8c590"}, 821 | {file = "pykdtree-1.3.12-cp39-cp39-win_arm64.whl", hash = "sha256:2e23bdaff080affc415b614f2cb230616fc4c4095b56bbb614c56b67f11f3ed1"}, 822 | {file = "pykdtree-1.3.12.tar.gz", hash = "sha256:cc20b2a67c64056485a314d2c2b6dba354af7ee1c8fb8dae1be6f2936a374341"}, 823 | ] 824 | 825 | [package.dependencies] 826 | numpy = "*" 827 | 828 | [[package]] 829 | name = "pymixbox" 830 | version = "2.0.0" 831 | description = "Pigment-Based Color Mixing" 832 | optional = false 833 | python-versions = "*" 834 | files = [ 835 | {file = "pymixbox-2.0.0-py2.py3-none-any.whl", hash = "sha256:ac645ee27989ee96622579250ccc057ede2cc57847c56c73082eae2e47341aa4"}, 836 | {file = "pymixbox-2.0.0.tar.gz", hash = "sha256:7f1fc8e950778efedfc20bbeeaf240576baca06726b3ce83a97ce507f226aee4"}, 837 | ] 838 | 839 | [[package]] 840 | name = "pyparsing" 841 | version = "3.1.2" 842 | description = "pyparsing module - Classes and methods to define and execute parsing grammars" 843 | optional = false 844 | python-versions = ">=3.6.8" 845 | files = [ 846 | {file = "pyparsing-3.1.2-py3-none-any.whl", hash = "sha256:f9db75911801ed778fe61bb643079ff86601aca99fcae6345aa67292038fb742"}, 847 | {file = "pyparsing-3.1.2.tar.gz", hash = "sha256:a1bac0ce561155ecc3ed78ca94d3c9378656ad4c94c1270de543f621420f94ad"}, 848 | ] 849 | 850 | [package.extras] 851 | diagrams = ["jinja2", "railroad-diagrams"] 852 | 853 | [[package]] 854 | name = "python-dateutil" 855 | version = "2.9.0.post0" 856 | description = "Extensions to the standard Python datetime module" 857 | optional = false 858 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" 859 | files = [ 860 | {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, 861 | {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, 862 | ] 863 | 864 | [package.dependencies] 865 | six = ">=1.5" 866 | 867 | [[package]] 868 | name = "requests" 869 | version = "2.32.3" 870 | description = "Python HTTP for Humans." 871 | optional = false 872 | python-versions = ">=3.8" 873 | files = [ 874 | {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, 875 | {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, 876 | ] 877 | 878 | [package.dependencies] 879 | certifi = ">=2017.4.17" 880 | charset-normalizer = ">=2,<4" 881 | idna = ">=2.5,<4" 882 | urllib3 = ">=1.21.1,<3" 883 | 884 | [package.extras] 885 | socks = ["PySocks (>=1.5.6,!=1.5.7)"] 886 | use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] 887 | 888 | [[package]] 889 | name = "rich" 890 | version = "13.7.1" 891 | description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" 892 | optional = false 893 | python-versions = ">=3.7.0" 894 | files = [ 895 | {file = "rich-13.7.1-py3-none-any.whl", hash = "sha256:4edbae314f59eb482f54e9e30bf00d33350aaa94f4bfcd4e9e3110e64d0d7222"}, 896 | {file = "rich-13.7.1.tar.gz", hash = "sha256:9be308cb1fe2f1f57d67ce99e95af38a1e2bc71ad9813b0e247cf7ffbcc3a432"}, 897 | ] 898 | 899 | [package.dependencies] 900 | markdown-it-py = ">=2.2.0" 901 | pygments = ">=2.13.0,<3.0.0" 902 | 903 | [package.extras] 904 | jupyter = ["ipywidgets (>=7.5.1,<9)"] 905 | 906 | [[package]] 907 | name = "scikit-learn" 908 | version = "1.5.1" 909 | description = "A set of python modules for machine learning and data mining" 910 | optional = false 911 | python-versions = ">=3.9" 912 | files = [ 913 | {file = "scikit_learn-1.5.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:781586c414f8cc58e71da4f3d7af311e0505a683e112f2f62919e3019abd3745"}, 914 | {file = "scikit_learn-1.5.1-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:f5b213bc29cc30a89a3130393b0e39c847a15d769d6e59539cd86b75d276b1a7"}, 915 | {file = "scikit_learn-1.5.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1ff4ba34c2abff5ec59c803ed1d97d61b036f659a17f55be102679e88f926fac"}, 916 | {file = "scikit_learn-1.5.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:161808750c267b77b4a9603cf9c93579c7a74ba8486b1336034c2f1579546d21"}, 917 | {file = "scikit_learn-1.5.1-cp310-cp310-win_amd64.whl", hash = "sha256:10e49170691514a94bb2e03787aa921b82dbc507a4ea1f20fd95557862c98dc1"}, 918 | {file = "scikit_learn-1.5.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:154297ee43c0b83af12464adeab378dee2d0a700ccd03979e2b821e7dd7cc1c2"}, 919 | {file = "scikit_learn-1.5.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:b5e865e9bd59396220de49cb4a57b17016256637c61b4c5cc81aaf16bc123bbe"}, 920 | {file = "scikit_learn-1.5.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:909144d50f367a513cee6090873ae582dba019cb3fca063b38054fa42704c3a4"}, 921 | {file = "scikit_learn-1.5.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:689b6f74b2c880276e365fe84fe4f1befd6a774f016339c65655eaff12e10cbf"}, 922 | {file = "scikit_learn-1.5.1-cp311-cp311-win_amd64.whl", hash = "sha256:9a07f90846313a7639af6a019d849ff72baadfa4c74c778821ae0fad07b7275b"}, 923 | {file = "scikit_learn-1.5.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5944ce1faada31c55fb2ba20a5346b88e36811aab504ccafb9f0339e9f780395"}, 924 | {file = "scikit_learn-1.5.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:0828673c5b520e879f2af6a9e99eee0eefea69a2188be1ca68a6121b809055c1"}, 925 | {file = "scikit_learn-1.5.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:508907e5f81390e16d754e8815f7497e52139162fd69c4fdbd2dfa5d6cc88915"}, 926 | {file = "scikit_learn-1.5.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:97625f217c5c0c5d0505fa2af28ae424bd37949bb2f16ace3ff5f2f81fb4498b"}, 927 | {file = "scikit_learn-1.5.1-cp312-cp312-win_amd64.whl", hash = "sha256:da3f404e9e284d2b0a157e1b56b6566a34eb2798205cba35a211df3296ab7a74"}, 928 | {file = "scikit_learn-1.5.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:88e0672c7ac21eb149d409c74cc29f1d611d5158175846e7a9c2427bd12b3956"}, 929 | {file = "scikit_learn-1.5.1-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:7b073a27797a283187a4ef4ee149959defc350b46cbf63a84d8514fe16b69855"}, 930 | {file = "scikit_learn-1.5.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b59e3e62d2be870e5c74af4e793293753565c7383ae82943b83383fdcf5cc5c1"}, 931 | {file = "scikit_learn-1.5.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1bd8d3a19d4bd6dc5a7d4f358c8c3a60934dc058f363c34c0ac1e9e12a31421d"}, 932 | {file = "scikit_learn-1.5.1-cp39-cp39-win_amd64.whl", hash = "sha256:5f57428de0c900a98389c4a433d4a3cf89de979b3aa24d1c1d251802aa15e44d"}, 933 | {file = "scikit_learn-1.5.1.tar.gz", hash = "sha256:0ea5d40c0e3951df445721927448755d3fe1d80833b0b7308ebff5d2a45e6414"}, 934 | ] 935 | 936 | [package.dependencies] 937 | joblib = ">=1.2.0" 938 | numpy = ">=1.19.5" 939 | scipy = ">=1.6.0" 940 | threadpoolctl = ">=3.1.0" 941 | 942 | [package.extras] 943 | benchmark = ["matplotlib (>=3.3.4)", "memory_profiler (>=0.57.0)", "pandas (>=1.1.5)"] 944 | build = ["cython (>=3.0.10)", "meson-python (>=0.16.0)", "numpy (>=1.19.5)", "scipy (>=1.6.0)"] 945 | docs = ["Pillow (>=7.1.2)", "matplotlib (>=3.3.4)", "memory_profiler (>=0.57.0)", "numpydoc (>=1.2.0)", "pandas (>=1.1.5)", "plotly (>=5.14.0)", "polars (>=0.20.23)", "pooch (>=1.6.0)", "pydata-sphinx-theme (>=0.15.3)", "scikit-image (>=0.17.2)", "seaborn (>=0.9.0)", "sphinx (>=7.3.7)", "sphinx-copybutton (>=0.5.2)", "sphinx-design (>=0.5.0)", "sphinx-gallery (>=0.16.0)", "sphinx-prompt (>=1.4.0)", "sphinx-remove-toctrees (>=1.0.0.post1)", "sphinxcontrib-sass (>=0.3.4)", "sphinxext-opengraph (>=0.9.1)"] 946 | examples = ["matplotlib (>=3.3.4)", "pandas (>=1.1.5)", "plotly (>=5.14.0)", "pooch (>=1.6.0)", "scikit-image (>=0.17.2)", "seaborn (>=0.9.0)"] 947 | install = ["joblib (>=1.2.0)", "numpy (>=1.19.5)", "scipy (>=1.6.0)", "threadpoolctl (>=3.1.0)"] 948 | maintenance = ["conda-lock (==2.5.6)"] 949 | tests = ["black (>=24.3.0)", "matplotlib (>=3.3.4)", "mypy (>=1.9)", "numpydoc (>=1.2.0)", "pandas (>=1.1.5)", "polars (>=0.20.23)", "pooch (>=1.6.0)", "pyamg (>=4.0.0)", "pyarrow (>=12.0.0)", "pytest (>=7.1.2)", "pytest-cov (>=2.9.0)", "ruff (>=0.2.1)", "scikit-image (>=0.17.2)"] 950 | 951 | [[package]] 952 | name = "scipy" 953 | version = "1.14.0" 954 | description = "Fundamental algorithms for scientific computing in Python" 955 | optional = false 956 | python-versions = ">=3.10" 957 | files = [ 958 | {file = "scipy-1.14.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7e911933d54ead4d557c02402710c2396529540b81dd554fc1ba270eb7308484"}, 959 | {file = "scipy-1.14.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:687af0a35462402dd851726295c1a5ae5f987bd6e9026f52e9505994e2f84ef6"}, 960 | {file = "scipy-1.14.0-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:07e179dc0205a50721022344fb85074f772eadbda1e1b3eecdc483f8033709b7"}, 961 | {file = "scipy-1.14.0-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:6a9c9a9b226d9a21e0a208bdb024c3982932e43811b62d202aaf1bb59af264b1"}, 962 | {file = "scipy-1.14.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:076c27284c768b84a45dcf2e914d4000aac537da74236a0d45d82c6fa4b7b3c0"}, 963 | {file = "scipy-1.14.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42470ea0195336df319741e230626b6225a740fd9dce9642ca13e98f667047c0"}, 964 | {file = "scipy-1.14.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:176c6f0d0470a32f1b2efaf40c3d37a24876cebf447498a4cefb947a79c21e9d"}, 965 | {file = "scipy-1.14.0-cp310-cp310-win_amd64.whl", hash = "sha256:ad36af9626d27a4326c8e884917b7ec321d8a1841cd6dacc67d2a9e90c2f0359"}, 966 | {file = "scipy-1.14.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6d056a8709ccda6cf36cdd2eac597d13bc03dba38360f418560a93050c76a16e"}, 967 | {file = "scipy-1.14.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:f0a50da861a7ec4573b7c716b2ebdcdf142b66b756a0d392c236ae568b3a93fb"}, 968 | {file = "scipy-1.14.0-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:94c164a9e2498e68308e6e148646e486d979f7fcdb8b4cf34b5441894bdb9caf"}, 969 | {file = "scipy-1.14.0-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:a7d46c3e0aea5c064e734c3eac5cf9eb1f8c4ceee756262f2c7327c4c2691c86"}, 970 | {file = "scipy-1.14.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9eee2989868e274aae26125345584254d97c56194c072ed96cb433f32f692ed8"}, 971 | {file = "scipy-1.14.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e3154691b9f7ed73778d746da2df67a19d046a6c8087c8b385bc4cdb2cfca74"}, 972 | {file = "scipy-1.14.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c40003d880f39c11c1edbae8144e3813904b10514cd3d3d00c277ae996488cdb"}, 973 | {file = "scipy-1.14.0-cp311-cp311-win_amd64.whl", hash = "sha256:5b083c8940028bb7e0b4172acafda6df762da1927b9091f9611b0bcd8676f2bc"}, 974 | {file = "scipy-1.14.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:bff2438ea1330e06e53c424893ec0072640dac00f29c6a43a575cbae4c99b2b9"}, 975 | {file = "scipy-1.14.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:bbc0471b5f22c11c389075d091d3885693fd3f5e9a54ce051b46308bc787e5d4"}, 976 | {file = "scipy-1.14.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:64b2ff514a98cf2bb734a9f90d32dc89dc6ad4a4a36a312cd0d6327170339eb0"}, 977 | {file = "scipy-1.14.0-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:7d3da42fbbbb860211a811782504f38ae7aaec9de8764a9bef6b262de7a2b50f"}, 978 | {file = "scipy-1.14.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d91db2c41dd6c20646af280355d41dfa1ec7eead235642178bd57635a3f82209"}, 979 | {file = "scipy-1.14.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a01cc03bcdc777c9da3cfdcc74b5a75caffb48a6c39c8450a9a05f82c4250a14"}, 980 | {file = "scipy-1.14.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:65df4da3c12a2bb9ad52b86b4dcf46813e869afb006e58be0f516bc370165159"}, 981 | {file = "scipy-1.14.0-cp312-cp312-win_amd64.whl", hash = "sha256:4c4161597c75043f7154238ef419c29a64ac4a7c889d588ea77690ac4d0d9b20"}, 982 | {file = "scipy-1.14.0.tar.gz", hash = "sha256:b5923f48cb840380f9854339176ef21763118a7300a88203ccd0bdd26e58527b"}, 983 | ] 984 | 985 | [package.dependencies] 986 | numpy = ">=1.23.5,<2.3" 987 | 988 | [package.extras] 989 | dev = ["cython-lint (>=0.12.2)", "doit (>=0.36.0)", "mypy (==1.10.0)", "pycodestyle", "pydevtool", "rich-click", "ruff (>=0.0.292)", "types-psutil", "typing_extensions"] 990 | doc = ["jupyterlite-pyodide-kernel", "jupyterlite-sphinx (>=0.13.1)", "jupytext", "matplotlib (>=3.5)", "myst-nb", "numpydoc", "pooch", "pydata-sphinx-theme (>=0.15.2)", "sphinx (>=5.0.0)", "sphinx-design (>=0.4.0)"] 991 | test = ["Cython", "array-api-strict", "asv", "gmpy2", "hypothesis (>=6.30)", "meson", "mpmath", "ninja", "pooch", "pytest", "pytest-cov", "pytest-timeout", "pytest-xdist", "scikit-umfpack", "threadpoolctl"] 992 | 993 | [[package]] 994 | name = "shellingham" 995 | version = "1.5.4" 996 | description = "Tool to Detect Surrounding Shell" 997 | optional = false 998 | python-versions = ">=3.7" 999 | files = [ 1000 | {file = "shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686"}, 1001 | {file = "shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de"}, 1002 | ] 1003 | 1004 | [[package]] 1005 | name = "six" 1006 | version = "1.16.0" 1007 | description = "Python 2 and 3 compatibility utilities" 1008 | optional = false 1009 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" 1010 | files = [ 1011 | {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, 1012 | {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, 1013 | ] 1014 | 1015 | [[package]] 1016 | name = "threadpoolctl" 1017 | version = "3.5.0" 1018 | description = "threadpoolctl" 1019 | optional = false 1020 | python-versions = ">=3.8" 1021 | files = [ 1022 | {file = "threadpoolctl-3.5.0-py3-none-any.whl", hash = "sha256:56c1e26c150397e58c4926da8eeee87533b1e32bef131bd4bf6a2f45f3185467"}, 1023 | {file = "threadpoolctl-3.5.0.tar.gz", hash = "sha256:082433502dd922bf738de0d8bcc4fdcbf0979ff44c42bd40f5af8a282f6fa107"}, 1024 | ] 1025 | 1026 | [[package]] 1027 | name = "toml" 1028 | version = "0.10.2" 1029 | description = "Python Library for Tom's Obvious, Minimal Language" 1030 | optional = false 1031 | python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" 1032 | files = [ 1033 | {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, 1034 | {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, 1035 | ] 1036 | 1037 | [[package]] 1038 | name = "typer" 1039 | version = "0.12.3" 1040 | description = "Typer, build great CLIs. Easy to code. Based on Python type hints." 1041 | optional = false 1042 | python-versions = ">=3.7" 1043 | files = [ 1044 | {file = "typer-0.12.3-py3-none-any.whl", hash = "sha256:070d7ca53f785acbccba8e7d28b08dcd88f79f1fbda035ade0aecec71ca5c914"}, 1045 | {file = "typer-0.12.3.tar.gz", hash = "sha256:49e73131481d804288ef62598d97a1ceef3058905aa536a1134f90891ba35482"}, 1046 | ] 1047 | 1048 | [package.dependencies] 1049 | click = ">=8.0.0" 1050 | rich = ">=10.11.0" 1051 | shellingham = ">=1.3.0" 1052 | typing-extensions = ">=3.7.4.3" 1053 | 1054 | [[package]] 1055 | name = "typing-extensions" 1056 | version = "4.12.2" 1057 | description = "Backported and Experimental Type Hints for Python 3.8+" 1058 | optional = false 1059 | python-versions = ">=3.8" 1060 | files = [ 1061 | {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, 1062 | {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, 1063 | ] 1064 | 1065 | [[package]] 1066 | name = "urllib3" 1067 | version = "2.2.2" 1068 | description = "HTTP library with thread-safe connection pooling, file post, and more." 1069 | optional = false 1070 | python-versions = ">=3.8" 1071 | files = [ 1072 | {file = "urllib3-2.2.2-py3-none-any.whl", hash = "sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472"}, 1073 | {file = "urllib3-2.2.2.tar.gz", hash = "sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168"}, 1074 | ] 1075 | 1076 | [package.extras] 1077 | brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] 1078 | h2 = ["h2 (>=4,<5)"] 1079 | socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] 1080 | zstd = ["zstandard (>=0.18.0)"] 1081 | 1082 | [metadata] 1083 | lock-version = "2.0" 1084 | python-versions = "~3.12" 1085 | content-hash = "9481cfb08b4e7d8d22e27e11f6b870a575a3440f2f8cf1015a55ca522d23b09b" 1086 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "palettesnap" 3 | version = "1.2.0" 4 | description = "A color palette generator" 5 | authors = ["EmperorEntropy"] 6 | readme = "README.md" 7 | license = "MIT" 8 | repository = "https://github.com/EmperorEntropy/PaletteSnap" 9 | 10 | [tool.poetry.scripts] 11 | palsnap = "palettesnap.cli:app" 12 | 13 | [tool.poetry.dependencies] 14 | python = "~3.12" 15 | toml = "^0.10.2" 16 | typer = "^0.12.3" 17 | scikit-learn = "^1.5.1" 18 | pykdtree = "^1.3.12" 19 | kmedoids = "^0.5.1" 20 | colour-science = "^0.4.4" 21 | rich = "^13.7.1" 22 | matplotlib = "^3.9.0" 23 | psutil = "^6.0.0" 24 | pymixbox = "^2.0.0" 25 | requests = "^2.32.3" 26 | 27 | [build-system] 28 | requires = ["poetry-core"] 29 | build-backend = "poetry.core.masonry.api" 30 | --------------------------------------------------------------------------------