diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile
new file mode 100644
index 0000000..a4c250f
--- /dev/null
+++ b/.devcontainer/Dockerfile
@@ -0,0 +1,9 @@
+FROM mcr.microsoft.com/devcontainers/base:ubuntu24.04
+# These dependencies were recommended by the Swiftly installer (2025-06)
+RUN apt-get update && apt-get -y install libpython3-dev libxml2-dev libncurses-dev libz3-dev pkg-config
+# Ugly installer: https://www.swift.org/install/linux/
+WORKDIR /
+RUN curl -L https://download.swift.org/swiftly/linux/swiftly-$(uname -m).tar.gz | tar zx
+USER vscode
+# Latest version is fine in dev, ci.yml will test older versions
+RUN ./swiftly init --assume-yes
diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
new file mode 100644
index 0000000..f177466
--- /dev/null
+++ b/.devcontainer/devcontainer.json
@@ -0,0 +1,14 @@
+{
+ "build": {
+ "dockerfile": "Dockerfile"
+ },
+ "customizations": {
+ "vscode": {
+ "extensions": [
+ "swiftlang.swift-vscode",
+ "github.vscode-github-actions",
+ "mhutchie.git-graph"
+ ]
+ }
+ }
+}
\ No newline at end of file
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index abbe926..06adff0 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -1,20 +1,25 @@
-name: Test
+name: CI
on:
- workflow_dispatch
- - push
+ - pull_request
jobs:
+ # https://github.com/swift-actions/setup-swift?tab=readme-ov-file#usage
test:
- runs-on: macos-13
+ name: Swift ${{ matrix.swift }} on ${{ matrix.os }}
+ runs-on: ${{ matrix.os }}
strategy:
matrix:
- params: [ '-scheme HSLuvMac -sdk macosx', '-scheme HSLuviOS -sdk iphonesimulator -destination "platform=iOS Simulator,name=iPhone 14"' ]
+ os: [ubuntu-latest, macos-latest, windows-latest]
+ swift: ["5", "6"]
steps:
+ # Perhaps we should switch to swift-actions/setup-swift, but they are undergoing a major refactor
+ # I tested swift-actions/setup-swift@next but got gpg errors, so we should wait (2025-06-14)
+ # For now this seems to be the best third party: https://github.com/SwiftyLab/setup-swift
+ - uses: SwiftyLab/setup-swift@v1
+ with:
+ swift-version: ${{ matrix.swift }}
- uses: actions/checkout@v4
- # https://github.com/actions/runner-images/discussions/8367#discussioncomment-12685592
- # https://github.com/actions/runner-images/blob/main/images/macos/macos-13-Readme.md
- - name: Select XCode
- run: sudo xcode-select -s /Applications/Xcode_14.1.app
- - name: Test
- run: xcodebuild ${{ matrix.params }} CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO test
+ - name: Build and Test
+ run: swift test
diff --git a/.gitignore b/.gitignore
index 553153a..e485cef 100644
--- a/.gitignore
+++ b/.gitignore
@@ -23,3 +23,6 @@ DerivedData
Carthage.checkout
Carthage.build
Carthage/Build
+
+.build
+.index-build
diff --git a/.swiftlint.yml b/.swiftlint.yml
deleted file mode 100644
index aa42cc7..0000000
--- a/.swiftlint.yml
+++ /dev/null
@@ -1,14 +0,0 @@
-disabled_rules: # rule identifiers to exclude from running
- - identifier_name
- - large_tuple
-
-opt_in_rules: # some rules are only opt-in
- - empty_count
-
-excluded: # paths to ignore during linting. Takes precedence over `included`.
- - Carthage
- - Pods
-
-force_cast: warning # implicitly
-force_try:
- severity: warning # explicitly
diff --git a/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata b/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
deleted file mode 100644
index 919434a..0000000
--- a/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
-
diff --git a/.swiftpm/xcode/package.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/.swiftpm/xcode/package.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
deleted file mode 100644
index 08de0be..0000000
--- a/.swiftpm/xcode/package.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
- IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded
-
-
-
diff --git a/.xctool-args b/.xctool-args
deleted file mode 100644
index 233de07..0000000
--- a/.xctool-args
+++ /dev/null
@@ -1,4 +0,0 @@
-[
- "-workspace", "HSLuvSwift.xcworkspace",
- "-configuration", "Debug",
-]
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..dd12d64
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,63 @@
+# Changelog
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
+and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## [3.0.0] - Unreleased
+### Added
+- New cross-platform API with no heap allocation
+- DevContainer support for development
+
+### Removed
+- **BREAKING**: UIKit/AppKit specific extensions
+- CocoaPods support
+- Carthage support
+- SwiftLint configuration
+- Xcode build automation
+- Playground examples
+
+## [2.1.0] - 2020-09-03
+### Added
+- Swift Package Manager support
+- HPLuv color initializers
+- Protocol conformances to UIColor extension
+
+### Changed
+- Updated for Xcode 12 and Swift 5
+
+## [2.0.0] - 2017-02-19
+### Changed
+- **BREAKING**: Renamed project from HUSL to HSLuv
+- Updated for Swift 3
+
+## [1.2.0] - 2015-10-20
+### Changed
+- Updated for Xcode 7 and Swift 2
+- Uses new Mirror syntax
+- Fixed Xcode warnings for var arguments and 'Always Search User Paths'
+
+## [1.1.0] - 2015-07-03
+### Changed
+- Removed CoreGraphics dependency
+- Made NS/UIColor initialization non-optional
+- Simplified CocoaPods by removing subspecs
+
+## [1.0.1] - 2015-07-02
+### Changed
+- Lowered deployment target in podspec
+
+## [1.0.0] - 2015-06-23
+### Added
+- Initial release of HSLuv for Swift
+- NS/UIColor extensions for macOS and iOS
+- CocoaPods and Carthage support
+- Playground examples
+
+[3.0.0]: https://github.com/hsluv/hsluv-swift/compare/v2.1.0...HEAD
+[2.1.0]: https://github.com/hsluv/hsluv-swift/compare/v2.0.0...v2.1.0
+[2.0.0]: https://github.com/hsluv/hsluv-swift/compare/v1.2.0...v2.0.0
+[1.2.0]: https://github.com/hsluv/hsluv-swift/compare/v1.1.0...v1.2.0
+[1.1.0]: https://github.com/hsluv/hsluv-swift/compare/v1.0.1...v1.1.0
+[1.0.1]: https://github.com/hsluv/hsluv-swift/compare/v1.0.0...v1.0.1
+[1.0.0]: https://github.com/hsluv/hsluv-swift/releases/tag/v1.0.0
\ No newline at end of file
diff --git a/HSLuvSwift.podspec b/HSLuvSwift.podspec
deleted file mode 100644
index bae13b1..0000000
--- a/HSLuvSwift.podspec
+++ /dev/null
@@ -1,23 +0,0 @@
-Pod::Spec.new do |s|
- s.name = "HSLuvSwift"
-
- s.version = "2.2.0"
-
- s.summary = "Swift port of HSLuv, a human-friendly alternative to HSL"
- s.homepage = "https://github.com/hsluv/hsluv-swift"
- s.license = { :type => 'MIT', :text => '@see LICENSE' }
- s.author = { "Clay Smith" => "s.clay.smith@gmail.com", "Alexei Boronine" => "alexei@boronine.com" }
- s.source = { :git => "https://github.com/hsluv/hsluv-swift.git", :tag => "v" + s.version.to_s }
- s.requires_arc = true
- s.xcconfig = { 'SWIFT_INSTALL_OBJC_HEADER' => 'NO' }
-
- s.source_files = 'Sources/HSLuvSwift/*.{swift}'
- s.frameworks = 'Foundation'
- s.swift_version = '5.0'
-
- s.ios.deployment_target = '10.0'
- s.ios.frameworks = 'UIKit'
-
- s.osx.deployment_target = '10.11'
- s.osx.frameworks = 'AppKit'
-end
diff --git a/HSLuvSwift.xcodeproj/project.pbxproj b/HSLuvSwift.xcodeproj/project.pbxproj
deleted file mode 100644
index cd5a539..0000000
--- a/HSLuvSwift.xcodeproj/project.pbxproj
+++ /dev/null
@@ -1,917 +0,0 @@
-// !$*UTF8*$!
-{
- archiveVersion = 1;
- classes = {
- };
- objectVersion = 47;
- objects = {
-
-/* Begin PBXBuildFile section */
- 3A7F6F7725019B0600B57E5B /* Protocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A7F6F7625019B0600B57E5B /* Protocols.swift */; };
- 3A7F6F7825019B0600B57E5B /* Protocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A7F6F7625019B0600B57E5B /* Protocols.swift */; };
- 3ABA53B82402EE2300F37E85 /* Color+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ABA53B22402EE2300F37E85 /* Color+Extensions.swift */; };
- 3ABA53B92402EE2300F37E85 /* Color+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ABA53B22402EE2300F37E85 /* Color+Extensions.swift */; };
- 3ABA53BA2402EE2300F37E85 /* ColorEncodings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ABA53B32402EE2300F37E85 /* ColorEncodings.swift */; };
- 3ABA53BB2402EE2300F37E85 /* ColorEncodings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ABA53B32402EE2300F37E85 /* ColorEncodings.swift */; };
- 3ABA53BC2402EE2300F37E85 /* Math.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ABA53B42402EE2300F37E85 /* Math.swift */; };
- 3ABA53BD2402EE2300F37E85 /* Math.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ABA53B42402EE2300F37E85 /* Math.swift */; };
- 3ABA53BE2402EE2300F37E85 /* Constant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ABA53B52402EE2300F37E85 /* Constant.swift */; };
- 3ABA53BF2402EE2300F37E85 /* Constant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ABA53B52402EE2300F37E85 /* Constant.swift */; };
- CB24CBB51B394AD800091467 /* Snapshot.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB328E391B3205050061A872 /* Snapshot.swift */; };
- CB24CBB61B394ADB00091467 /* Snapshot.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB328E391B3205050061A872 /* Snapshot.swift */; };
- CB328E3B1B32050C0061A872 /* Snapshot.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB328E391B3205050061A872 /* Snapshot.swift */; };
- CB339EEC1B31EE2D0067FDAF /* snapshot-rev4.json in Resources */ = {isa = PBXBuildFile; fileRef = CB339EEB1B31EE210067FDAF /* snapshot-rev4.json */; };
- CB6AB2841B3083890075AF04 /* HSLuvSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CBDA801C1B2D2C740010C653 /* HSLuvSwift.framework */; };
- CB6AB28B1B3084020075AF04 /* AppKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB783AD11B3078D5001F8BCB /* AppKit.swift */; };
- CB6AB2A61B308A6F0075AF04 /* HSLuvSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CB37B9051B2E7994009E63A5 /* HSLuvSwift.framework */; };
- CB6AB2AD1B308B520075AF04 /* UIKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB783AD01B3078CF001F8BCB /* UIKit.swift */; };
- CB98BFB91B307B070027506A /* HSLuvSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CBDA801C1B2D2C740010C653 /* HSLuvSwift.framework */; };
- CB98BFBF1B307B1D0027506A /* HSLuvTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB604FB91B30734C009A99D1 /* HSLuvTests.swift */; };
-/* End PBXBuildFile section */
-
-/* Begin PBXContainerItemProxy section */
- CB339EED1B31F9D20067FDAF /* PBXContainerItemProxy */ = {
- isa = PBXContainerItemProxy;
- containerPortal = CBDA80131B2D2C740010C653 /* Project object */;
- proxyType = 1;
- remoteGlobalIDString = CBDA801B1B2D2C740010C653;
- remoteInfo = HSLuvMac;
- };
- CB339EEF1B31F9DA0067FDAF /* PBXContainerItemProxy */ = {
- isa = PBXContainerItemProxy;
- containerPortal = CBDA80131B2D2C740010C653 /* Project object */;
- proxyType = 1;
- remoteGlobalIDString = CBDA801B1B2D2C740010C653;
- remoteInfo = HSLuvMac;
- };
- CB339EF11B31F9E10067FDAF /* PBXContainerItemProxy */ = {
- isa = PBXContainerItemProxy;
- containerPortal = CBDA80131B2D2C740010C653 /* Project object */;
- proxyType = 1;
- remoteGlobalIDString = CB37B9041B2E7994009E63A5;
- remoteInfo = HSLuviOS;
- };
-/* End PBXContainerItemProxy section */
-
-/* Begin PBXFileReference section */
- 3A41989C2502248100DD4ED1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
- 3A7F6F7625019B0600B57E5B /* Protocols.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Protocols.swift; sourceTree = ""; };
- 3ABA53B12402EE2300F37E85 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
- 3ABA53B22402EE2300F37E85 /* Color+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Color+Extensions.swift"; sourceTree = ""; };
- 3ABA53B32402EE2300F37E85 /* ColorEncodings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ColorEncodings.swift; sourceTree = ""; };
- 3ABA53B42402EE2300F37E85 /* Math.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Math.swift; sourceTree = ""; };
- 3ABA53B52402EE2300F37E85 /* Constant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Constant.swift; sourceTree = ""; };
- CB328E391B3205050061A872 /* Snapshot.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Snapshot.swift; path = Tests/HSLuvSwiftTests/Snapshot.swift; sourceTree = SOURCE_ROOT; };
- CB339EEB1B31EE210067FDAF /* snapshot-rev4.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; name = "snapshot-rev4.json"; path = "Tests/HSLuvSwiftTests/Resources/snapshot-rev4.json"; sourceTree = SOURCE_ROOT; };
- CB37B9051B2E7994009E63A5 /* HSLuvSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = HSLuvSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; };
- CB604FB91B30734C009A99D1 /* HSLuvTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = HSLuvTests.swift; path = Tests/HSLuvSwiftTests/HSLuvTests.swift; sourceTree = SOURCE_ROOT; };
- CB6AB27F1B3083890075AF04 /* HSLuvMacTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = HSLuvMacTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
- CB6AB2A11B308A6F0075AF04 /* HSLuviOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = HSLuviOSTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
- CB783AD01B3078CF001F8BCB /* UIKit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = UIKit.swift; path = Tests/HSLuvSwiftTests/iOSExtensions/UIKit.swift; sourceTree = SOURCE_ROOT; };
- CB783AD11B3078D5001F8BCB /* AppKit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AppKit.swift; path = Tests/HSLuvSwiftTests/MacExtensions/AppKit.swift; sourceTree = SOURCE_ROOT; };
- CB98BFB41B307B070027506A /* HSLuvTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = HSLuvTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
- CBCAD5371B2FBEBD00D0366A /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; };
- CBCAD53B1B2FBF0600D0366A /* HSLuvSwift.podspec */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = HSLuvSwift.podspec; sourceTree = ""; };
- CBDA801C1B2D2C740010C653 /* HSLuvSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = HSLuvSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; };
- CBDD2B131B461C3000E4548F /* LICENSE */ = {isa = PBXFileReference; lastKnownFileType = text; path = LICENSE; sourceTree = ""; };
-/* End PBXFileReference section */
-
-/* Begin PBXFrameworksBuildPhase section */
- CB37B9011B2E7994009E63A5 /* Frameworks */ = {
- isa = PBXFrameworksBuildPhase;
- buildActionMask = 2147483647;
- files = (
- );
- runOnlyForDeploymentPostprocessing = 0;
- };
- CB6AB27C1B3083890075AF04 /* Frameworks */ = {
- isa = PBXFrameworksBuildPhase;
- buildActionMask = 2147483647;
- files = (
- CB6AB2841B3083890075AF04 /* HSLuvSwift.framework in Frameworks */,
- );
- runOnlyForDeploymentPostprocessing = 0;
- };
- CB6AB29E1B308A6F0075AF04 /* Frameworks */ = {
- isa = PBXFrameworksBuildPhase;
- buildActionMask = 2147483647;
- files = (
- CB6AB2A61B308A6F0075AF04 /* HSLuvSwift.framework in Frameworks */,
- );
- runOnlyForDeploymentPostprocessing = 0;
- };
- CB98BFB11B307B070027506A /* Frameworks */ = {
- isa = PBXFrameworksBuildPhase;
- buildActionMask = 2147483647;
- files = (
- CB98BFB91B307B070027506A /* HSLuvSwift.framework in Frameworks */,
- );
- runOnlyForDeploymentPostprocessing = 0;
- };
- CBDA80181B2D2C740010C653 /* Frameworks */ = {
- isa = PBXFrameworksBuildPhase;
- buildActionMask = 2147483647;
- files = (
- );
- runOnlyForDeploymentPostprocessing = 0;
- };
-/* End PBXFrameworksBuildPhase section */
-
-/* Begin PBXGroup section */
- 3A46031B2409BF92008B7C02 /* HSLuvSwiftTests */ = {
- isa = PBXGroup;
- children = (
- CB604FB91B30734C009A99D1 /* HSLuvTests.swift */,
- CB328E391B3205050061A872 /* Snapshot.swift */,
- CBDA802A1B2D2C740010C653 /* AppKit */,
- CB604FAA1B3071DC009A99D1 /* iOS */,
- CBD1E3061B30B2C800E49F9B /* Resources */,
- 3A46031C2409C03A008B7C02 /* Supporting Files */,
- );
- path = HSLuvSwiftTests;
- sourceTree = "";
- };
- 3A46031C2409C03A008B7C02 /* Supporting Files */ = {
- isa = PBXGroup;
- children = (
- 3A41989C2502248100DD4ED1 /* Info.plist */,
- );
- name = "Supporting Files";
- sourceTree = "";
- };
- 3ABA53AE2402EC0200F37E85 /* HSLuvSwift */ = {
- isa = PBXGroup;
- children = (
- 3ABA53B32402EE2300F37E85 /* ColorEncodings.swift */,
- 3ABA53B52402EE2300F37E85 /* Constant.swift */,
- 3ABA53B42402EE2300F37E85 /* Math.swift */,
- 3A7F6F7625019B0600B57E5B /* Protocols.swift */,
- 3ABA53AF2402EC1900F37E85 /* Extensions */,
- 3ABA53B02402ED9900F37E85 /* Supporting Files */,
- );
- path = HSLuvSwift;
- sourceTree = "";
- };
- 3ABA53AF2402EC1900F37E85 /* Extensions */ = {
- isa = PBXGroup;
- children = (
- 3ABA53B22402EE2300F37E85 /* Color+Extensions.swift */,
- );
- name = Extensions;
- sourceTree = "";
- };
- 3ABA53B02402ED9900F37E85 /* Supporting Files */ = {
- isa = PBXGroup;
- children = (
- 3ABA53B12402EE2300F37E85 /* Info.plist */,
- );
- name = "Supporting Files";
- sourceTree = "";
- };
- CB37B9121B2E7994009E63A5 /* Tests */ = {
- isa = PBXGroup;
- children = (
- 3A46031B2409BF92008B7C02 /* HSLuvSwiftTests */,
- );
- path = Tests;
- sourceTree = "";
- };
- CB604FAA1B3071DC009A99D1 /* iOS */ = {
- isa = PBXGroup;
- children = (
- CB783AD01B3078CF001F8BCB /* UIKit.swift */,
- );
- name = iOS;
- path = ..;
- sourceTree = "";
- };
- CBCAD5361B2FBEAF00D0366A /* Podspec Metadata */ = {
- isa = PBXGroup;
- children = (
- CBCAD53B1B2FBF0600D0366A /* HSLuvSwift.podspec */,
- CBCAD5371B2FBEBD00D0366A /* README.md */,
- CBDD2B131B461C3000E4548F /* LICENSE */,
- );
- name = "Podspec Metadata";
- sourceTree = "";
- };
- CBD1E3061B30B2C800E49F9B /* Resources */ = {
- isa = PBXGroup;
- children = (
- CB339EEB1B31EE210067FDAF /* snapshot-rev4.json */,
- );
- path = Resources;
- sourceTree = "";
- };
- CBDA80121B2D2C740010C653 = {
- isa = PBXGroup;
- children = (
- CBDA801E1B2D2C740010C653 /* Sources */,
- CB37B9121B2E7994009E63A5 /* Tests */,
- CBCAD5361B2FBEAF00D0366A /* Podspec Metadata */,
- CBDA801D1B2D2C740010C653 /* Products */,
- );
- sourceTree = "";
- };
- CBDA801D1B2D2C740010C653 /* Products */ = {
- isa = PBXGroup;
- children = (
- CBDA801C1B2D2C740010C653 /* HSLuvSwift.framework */,
- CB37B9051B2E7994009E63A5 /* HSLuvSwift.framework */,
- CB98BFB41B307B070027506A /* HSLuvTests.xctest */,
- CB6AB27F1B3083890075AF04 /* HSLuvMacTests.xctest */,
- CB6AB2A11B308A6F0075AF04 /* HSLuviOSTests.xctest */,
- );
- name = Products;
- sourceTree = "";
- };
- CBDA801E1B2D2C740010C653 /* Sources */ = {
- isa = PBXGroup;
- children = (
- 3ABA53AE2402EC0200F37E85 /* HSLuvSwift */,
- );
- path = Sources;
- sourceTree = "";
- };
- CBDA802A1B2D2C740010C653 /* AppKit */ = {
- isa = PBXGroup;
- children = (
- CB783AD11B3078D5001F8BCB /* AppKit.swift */,
- );
- name = AppKit;
- path = ..;
- sourceTree = "";
- };
-/* End PBXGroup section */
-
-/* Begin PBXHeadersBuildPhase section */
- CB37B9021B2E7994009E63A5 /* Headers */ = {
- isa = PBXHeadersBuildPhase;
- buildActionMask = 2147483647;
- files = (
- );
- runOnlyForDeploymentPostprocessing = 0;
- };
- CBDA80191B2D2C740010C653 /* Headers */ = {
- isa = PBXHeadersBuildPhase;
- buildActionMask = 2147483647;
- files = (
- );
- runOnlyForDeploymentPostprocessing = 0;
- };
-/* End PBXHeadersBuildPhase section */
-
-/* Begin PBXNativeTarget section */
- CB37B9041B2E7994009E63A5 /* HSLuviOS */ = {
- isa = PBXNativeTarget;
- buildConfigurationList = CB37B9161B2E7995009E63A5 /* Build configuration list for PBXNativeTarget "HSLuviOS" */;
- buildPhases = (
- CB37B9001B2E7994009E63A5 /* Sources */,
- CB37B9011B2E7994009E63A5 /* Frameworks */,
- CB37B9021B2E7994009E63A5 /* Headers */,
- CB37B9031B2E7994009E63A5 /* Resources */,
- );
- buildRules = (
- );
- dependencies = (
- );
- name = HSLuviOS;
- productName = "HSLuvSwift iOS";
- productReference = CB37B9051B2E7994009E63A5 /* HSLuvSwift.framework */;
- productType = "com.apple.product-type.framework";
- };
- CB6AB27E1B3083890075AF04 /* HSLuvMacTests */ = {
- isa = PBXNativeTarget;
- buildConfigurationList = CB6AB2891B3083890075AF04 /* Build configuration list for PBXNativeTarget "HSLuvMacTests" */;
- buildPhases = (
- CB6AB27B1B3083890075AF04 /* Sources */,
- CB6AB27C1B3083890075AF04 /* Frameworks */,
- CB6AB27D1B3083890075AF04 /* Resources */,
- );
- buildRules = (
- );
- dependencies = (
- CB339EF01B31F9DA0067FDAF /* PBXTargetDependency */,
- );
- name = HSLuvMacTests;
- productName = HSLuvMacTests;
- productReference = CB6AB27F1B3083890075AF04 /* HSLuvMacTests.xctest */;
- productType = "com.apple.product-type.bundle.unit-test";
- };
- CB6AB2A01B308A6F0075AF04 /* HSLuviOSTests */ = {
- isa = PBXNativeTarget;
- buildConfigurationList = CB6AB2A91B308A6F0075AF04 /* Build configuration list for PBXNativeTarget "HSLuviOSTests" */;
- buildPhases = (
- CB6AB29D1B308A6F0075AF04 /* Sources */,
- CB6AB29E1B308A6F0075AF04 /* Frameworks */,
- CB6AB29F1B308A6F0075AF04 /* Resources */,
- );
- buildRules = (
- );
- dependencies = (
- CB339EF21B31F9E10067FDAF /* PBXTargetDependency */,
- );
- name = HSLuviOSTests;
- productName = HSLuviOSTests;
- productReference = CB6AB2A11B308A6F0075AF04 /* HSLuviOSTests.xctest */;
- productType = "com.apple.product-type.bundle.unit-test";
- };
- CB98BFB31B307B070027506A /* HSLuvTests */ = {
- isa = PBXNativeTarget;
- buildConfigurationList = CB98BFBC1B307B070027506A /* Build configuration list for PBXNativeTarget "HSLuvTests" */;
- buildPhases = (
- CB98BFB01B307B070027506A /* Sources */,
- CB98BFB11B307B070027506A /* Frameworks */,
- CB98BFB21B307B070027506A /* Resources */,
- );
- buildRules = (
- );
- dependencies = (
- CB339EEE1B31F9D20067FDAF /* PBXTargetDependency */,
- );
- name = HSLuvTests;
- productName = HSLuvTests;
- productReference = CB98BFB41B307B070027506A /* HSLuvTests.xctest */;
- productType = "com.apple.product-type.bundle.unit-test";
- };
- CBDA801B1B2D2C740010C653 /* HSLuvMac */ = {
- isa = PBXNativeTarget;
- buildConfigurationList = CBDA80301B2D2C740010C653 /* Build configuration list for PBXNativeTarget "HSLuvMac" */;
- buildPhases = (
- CBDA80171B2D2C740010C653 /* Sources */,
- CBDA80181B2D2C740010C653 /* Frameworks */,
- CBDA80191B2D2C740010C653 /* Headers */,
- CBDA801A1B2D2C740010C653 /* Resources */,
- );
- buildRules = (
- );
- dependencies = (
- );
- name = HSLuvMac;
- productName = HSLuvSwift;
- productReference = CBDA801C1B2D2C740010C653 /* HSLuvSwift.framework */;
- productType = "com.apple.product-type.framework";
- };
-/* End PBXNativeTarget section */
-
-/* Begin PBXProject section */
- CBDA80131B2D2C740010C653 /* Project object */ = {
- isa = PBXProject;
- attributes = {
- LastSwiftMigration = 0700;
- LastSwiftUpdateCheck = 0700;
- LastUpgradeCheck = 1200;
- ORGANIZATIONNAME = "Clay Smith";
- TargetAttributes = {
- CB37B9041B2E7994009E63A5 = {
- CreatedOnToolsVersion = 7.0;
- LastSwiftMigration = 1130;
- ProvisioningStyle = Automatic;
- };
- CB6AB27E1B3083890075AF04 = {
- CreatedOnToolsVersion = 7.0;
- LastSwiftMigration = 1130;
- ProvisioningStyle = Automatic;
- };
- CB6AB2A01B308A6F0075AF04 = {
- CreatedOnToolsVersion = 7.0;
- LastSwiftMigration = 1130;
- };
- CB98BFB31B307B070027506A = {
- CreatedOnToolsVersion = 7.0;
- LastSwiftMigration = 1130;
- };
- CBDA801B1B2D2C740010C653 = {
- CreatedOnToolsVersion = 7.0;
- LastSwiftMigration = 1130;
- ProvisioningStyle = Automatic;
- };
- };
- };
- buildConfigurationList = CBDA80161B2D2C740010C653 /* Build configuration list for PBXProject "HSLuvSwift" */;
- compatibilityVersion = "Xcode 6.3";
- developmentRegion = en;
- hasScannedForEncodings = 0;
- knownRegions = (
- Base,
- en,
- );
- mainGroup = CBDA80121B2D2C740010C653;
- productRefGroup = CBDA801D1B2D2C740010C653 /* Products */;
- projectDirPath = "";
- projectRoot = "";
- targets = (
- CBDA801B1B2D2C740010C653 /* HSLuvMac */,
- CB37B9041B2E7994009E63A5 /* HSLuviOS */,
- CB98BFB31B307B070027506A /* HSLuvTests */,
- CB6AB27E1B3083890075AF04 /* HSLuvMacTests */,
- CB6AB2A01B308A6F0075AF04 /* HSLuviOSTests */,
- );
- };
-/* End PBXProject section */
-
-/* Begin PBXResourcesBuildPhase section */
- CB37B9031B2E7994009E63A5 /* Resources */ = {
- isa = PBXResourcesBuildPhase;
- buildActionMask = 2147483647;
- files = (
- );
- runOnlyForDeploymentPostprocessing = 0;
- };
- CB6AB27D1B3083890075AF04 /* Resources */ = {
- isa = PBXResourcesBuildPhase;
- buildActionMask = 2147483647;
- files = (
- );
- runOnlyForDeploymentPostprocessing = 0;
- };
- CB6AB29F1B308A6F0075AF04 /* Resources */ = {
- isa = PBXResourcesBuildPhase;
- buildActionMask = 2147483647;
- files = (
- );
- runOnlyForDeploymentPostprocessing = 0;
- };
- CB98BFB21B307B070027506A /* Resources */ = {
- isa = PBXResourcesBuildPhase;
- buildActionMask = 2147483647;
- files = (
- CB339EEC1B31EE2D0067FDAF /* snapshot-rev4.json in Resources */,
- );
- runOnlyForDeploymentPostprocessing = 0;
- };
- CBDA801A1B2D2C740010C653 /* Resources */ = {
- isa = PBXResourcesBuildPhase;
- buildActionMask = 2147483647;
- files = (
- );
- runOnlyForDeploymentPostprocessing = 0;
- };
-/* End PBXResourcesBuildPhase section */
-
-/* Begin PBXSourcesBuildPhase section */
- CB37B9001B2E7994009E63A5 /* Sources */ = {
- isa = PBXSourcesBuildPhase;
- buildActionMask = 2147483647;
- files = (
- 3A7F6F7825019B0600B57E5B /* Protocols.swift in Sources */,
- 3ABA53BB2402EE2300F37E85 /* ColorEncodings.swift in Sources */,
- 3ABA53B92402EE2300F37E85 /* Color+Extensions.swift in Sources */,
- 3ABA53BD2402EE2300F37E85 /* Math.swift in Sources */,
- 3ABA53BF2402EE2300F37E85 /* Constant.swift in Sources */,
- );
- runOnlyForDeploymentPostprocessing = 0;
- };
- CB6AB27B1B3083890075AF04 /* Sources */ = {
- isa = PBXSourcesBuildPhase;
- buildActionMask = 2147483647;
- files = (
- CB6AB28B1B3084020075AF04 /* AppKit.swift in Sources */,
- CB24CBB51B394AD800091467 /* Snapshot.swift in Sources */,
- );
- runOnlyForDeploymentPostprocessing = 0;
- };
- CB6AB29D1B308A6F0075AF04 /* Sources */ = {
- isa = PBXSourcesBuildPhase;
- buildActionMask = 2147483647;
- files = (
- CB6AB2AD1B308B520075AF04 /* UIKit.swift in Sources */,
- CB24CBB61B394ADB00091467 /* Snapshot.swift in Sources */,
- );
- runOnlyForDeploymentPostprocessing = 0;
- };
- CB98BFB01B307B070027506A /* Sources */ = {
- isa = PBXSourcesBuildPhase;
- buildActionMask = 2147483647;
- files = (
- CB98BFBF1B307B1D0027506A /* HSLuvTests.swift in Sources */,
- CB328E3B1B32050C0061A872 /* Snapshot.swift in Sources */,
- );
- runOnlyForDeploymentPostprocessing = 0;
- };
- CBDA80171B2D2C740010C653 /* Sources */ = {
- isa = PBXSourcesBuildPhase;
- buildActionMask = 2147483647;
- files = (
- 3A7F6F7725019B0600B57E5B /* Protocols.swift in Sources */,
- 3ABA53BA2402EE2300F37E85 /* ColorEncodings.swift in Sources */,
- 3ABA53B82402EE2300F37E85 /* Color+Extensions.swift in Sources */,
- 3ABA53BC2402EE2300F37E85 /* Math.swift in Sources */,
- 3ABA53BE2402EE2300F37E85 /* Constant.swift in Sources */,
- );
- runOnlyForDeploymentPostprocessing = 0;
- };
-/* End PBXSourcesBuildPhase section */
-
-/* Begin PBXTargetDependency section */
- CB339EEE1B31F9D20067FDAF /* PBXTargetDependency */ = {
- isa = PBXTargetDependency;
- target = CBDA801B1B2D2C740010C653 /* HSLuvMac */;
- targetProxy = CB339EED1B31F9D20067FDAF /* PBXContainerItemProxy */;
- };
- CB339EF01B31F9DA0067FDAF /* PBXTargetDependency */ = {
- isa = PBXTargetDependency;
- target = CBDA801B1B2D2C740010C653 /* HSLuvMac */;
- targetProxy = CB339EEF1B31F9DA0067FDAF /* PBXContainerItemProxy */;
- };
- CB339EF21B31F9E10067FDAF /* PBXTargetDependency */ = {
- isa = PBXTargetDependency;
- target = CB37B9041B2E7994009E63A5 /* HSLuviOS */;
- targetProxy = CB339EF11B31F9E10067FDAF /* PBXContainerItemProxy */;
- };
-/* End PBXTargetDependency section */
-
-/* Begin XCBuildConfiguration section */
- CB37B9171B2E7995009E63A5 /* Debug */ = {
- isa = XCBuildConfiguration;
- buildSettings = {
- CLANG_ENABLE_MODULES = YES;
- CODE_SIGN_IDENTITY = "";
- "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
- CODE_SIGN_STYLE = Automatic;
- DEFINES_MODULE = YES;
- DEVELOPMENT_TEAM = "";
- DYLIB_COMPATIBILITY_VERSION = 1;
- DYLIB_CURRENT_VERSION = 1;
- DYLIB_INSTALL_NAME_BASE = "@rpath";
- INFOPLIST_FILE = "$(SRCROOT)/Sources/$(PROJECT_NAME)/Info.plist";
- INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
- LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
- MARKETING_VERSION = 2.2;
- PRODUCT_BUNDLE_IDENTIFIER = com.claysmith.HSLuvSwiftiOS;
- PRODUCT_NAME = HSLuvSwift;
- PROVISIONING_PROFILE_SPECIFIER = "";
- SDKROOT = iphoneos;
- SKIP_INSTALL = YES;
- SUPPORTED_PLATFORMS = "iphonesimulator iphoneos";
- SWIFT_OPTIMIZATION_LEVEL = "-Onone";
- SWIFT_SWIFT3_OBJC_INFERENCE = Default;
- SWIFT_VERSION = 5.0;
- TARGETED_DEVICE_FAMILY = "1,2";
- };
- name = Debug;
- };
- CB37B9181B2E7995009E63A5 /* Release */ = {
- isa = XCBuildConfiguration;
- buildSettings = {
- CLANG_ENABLE_MODULES = YES;
- CODE_SIGN_IDENTITY = "";
- "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
- CODE_SIGN_STYLE = Automatic;
- DEFINES_MODULE = YES;
- DEVELOPMENT_TEAM = "";
- DYLIB_COMPATIBILITY_VERSION = 1;
- DYLIB_CURRENT_VERSION = 1;
- DYLIB_INSTALL_NAME_BASE = "@rpath";
- INFOPLIST_FILE = "$(SRCROOT)/Sources/$(PROJECT_NAME)/Info.plist";
- INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
- LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
- MARKETING_VERSION = 2.2;
- PRODUCT_BUNDLE_IDENTIFIER = com.claysmith.HSLuvSwiftiOS;
- PRODUCT_NAME = HSLuvSwift;
- PROVISIONING_PROFILE_SPECIFIER = "";
- SDKROOT = iphoneos;
- SKIP_INSTALL = YES;
- SUPPORTED_PLATFORMS = "iphonesimulator iphoneos";
- SWIFT_SWIFT3_OBJC_INFERENCE = Default;
- SWIFT_VERSION = 5.0;
- TARGETED_DEVICE_FAMILY = "1,2";
- VALIDATE_PRODUCT = YES;
- };
- name = Release;
- };
- CB6AB2871B3083890075AF04 /* Debug */ = {
- isa = XCBuildConfiguration;
- buildSettings = {
- ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
- ALWAYS_SEARCH_USER_PATHS = NO;
- CODE_SIGN_IDENTITY = "-";
- CODE_SIGN_STYLE = Automatic;
- COMBINE_HIDPI_IMAGES = YES;
- DEVELOPMENT_TEAM = "";
- INFOPLIST_FILE = Tests/HSLuvSwiftTests/Info.plist;
- LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
- PRODUCT_BUNDLE_IDENTIFIER = com.claysmith.HSLuvMacTests;
- PRODUCT_NAME = "$(TARGET_NAME)";
- PROVISIONING_PROFILE_SPECIFIER = "";
- SWIFT_SWIFT3_OBJC_INFERENCE = Default;
- SWIFT_VERSION = 5.0;
- };
- name = Debug;
- };
- CB6AB2881B3083890075AF04 /* Release */ = {
- isa = XCBuildConfiguration;
- buildSettings = {
- ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
- ALWAYS_SEARCH_USER_PATHS = NO;
- CODE_SIGN_IDENTITY = "-";
- CODE_SIGN_STYLE = Automatic;
- COMBINE_HIDPI_IMAGES = YES;
- DEVELOPMENT_TEAM = "";
- INFOPLIST_FILE = Tests/HSLuvSwiftTests/Info.plist;
- LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
- PRODUCT_BUNDLE_IDENTIFIER = com.claysmith.HSLuvMacTests;
- PRODUCT_NAME = "$(TARGET_NAME)";
- PROVISIONING_PROFILE_SPECIFIER = "";
- SWIFT_SWIFT3_OBJC_INFERENCE = Default;
- SWIFT_VERSION = 5.0;
- };
- name = Release;
- };
- CB6AB2AA1B308A6F0075AF04 /* Debug */ = {
- isa = XCBuildConfiguration;
- buildSettings = {
- ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
- ALWAYS_SEARCH_USER_PATHS = NO;
- CODE_SIGN_IDENTITY = "iPhone Developer";
- "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
- DEVELOPMENT_TEAM = "";
- INFOPLIST_FILE = Tests/HSLuvSwiftTests/Info.plist;
- LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
- PRODUCT_BUNDLE_IDENTIFIER = com.claysmith.HSLuviOSTests;
- PRODUCT_NAME = "$(TARGET_NAME)";
- SDKROOT = iphoneos;
- SWIFT_SWIFT3_OBJC_INFERENCE = Default;
- SWIFT_VERSION = 5.0;
- };
- name = Debug;
- };
- CB6AB2AB1B308A6F0075AF04 /* Release */ = {
- isa = XCBuildConfiguration;
- buildSettings = {
- ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
- ALWAYS_SEARCH_USER_PATHS = NO;
- CODE_SIGN_IDENTITY = "iPhone Developer";
- "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
- DEVELOPMENT_TEAM = "";
- INFOPLIST_FILE = Tests/HSLuvSwiftTests/Info.plist;
- LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
- PRODUCT_BUNDLE_IDENTIFIER = com.claysmith.HSLuviOSTests;
- PRODUCT_NAME = "$(TARGET_NAME)";
- SDKROOT = iphoneos;
- SWIFT_SWIFT3_OBJC_INFERENCE = Default;
- SWIFT_VERSION = 5.0;
- VALIDATE_PRODUCT = YES;
- };
- name = Release;
- };
- CB98BFBD1B307B070027506A /* Debug */ = {
- isa = XCBuildConfiguration;
- buildSettings = {
- ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
- ALWAYS_SEARCH_USER_PATHS = NO;
- CODE_SIGN_IDENTITY = "-";
- COMBINE_HIDPI_IMAGES = YES;
- DEVELOPMENT_TEAM = "";
- INFOPLIST_FILE = Tests/HSLuvSwiftTests/Info.plist;
- LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
- PRODUCT_BUNDLE_IDENTIFIER = com.claysmith.HSLuvTests;
- PRODUCT_NAME = "$(TARGET_NAME)";
- SWIFT_SWIFT3_OBJC_INFERENCE = Default;
- SWIFT_VERSION = 5.0;
- };
- name = Debug;
- };
- CB98BFBE1B307B070027506A /* Release */ = {
- isa = XCBuildConfiguration;
- buildSettings = {
- ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
- ALWAYS_SEARCH_USER_PATHS = NO;
- CODE_SIGN_IDENTITY = "-";
- COMBINE_HIDPI_IMAGES = YES;
- DEVELOPMENT_TEAM = "";
- INFOPLIST_FILE = Tests/HSLuvSwiftTests/Info.plist;
- LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
- PRODUCT_BUNDLE_IDENTIFIER = com.claysmith.HSLuvTests;
- PRODUCT_NAME = "$(TARGET_NAME)";
- SWIFT_SWIFT3_OBJC_INFERENCE = Default;
- SWIFT_VERSION = 5.0;
- };
- name = Release;
- };
- CBDA802E1B2D2C740010C653 /* Debug */ = {
- isa = XCBuildConfiguration;
- buildSettings = {
- ALWAYS_SEARCH_USER_PATHS = NO;
- CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
- CLANG_CXX_LIBRARY = "libc++";
- CLANG_ENABLE_MODULES = YES;
- CLANG_ENABLE_OBJC_ARC = YES;
- CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
- CLANG_WARN_BOOL_CONVERSION = YES;
- CLANG_WARN_COMMA = YES;
- CLANG_WARN_CONSTANT_CONVERSION = YES;
- CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
- CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_EMPTY_BODY = YES;
- CLANG_WARN_ENUM_CONVERSION = YES;
- CLANG_WARN_INFINITE_RECURSION = YES;
- CLANG_WARN_INT_CONVERSION = YES;
- CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
- CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
- CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
- CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
- CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
- CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
- CLANG_WARN_STRICT_PROTOTYPES = YES;
- CLANG_WARN_SUSPICIOUS_MOVE = YES;
- CLANG_WARN_UNREACHABLE_CODE = YES;
- CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
- COPY_PHASE_STRIP = NO;
- CURRENT_PROJECT_VERSION = 1;
- DEBUG_INFORMATION_FORMAT = dwarf;
- ENABLE_STRICT_OBJC_MSGSEND = YES;
- ENABLE_TESTABILITY = YES;
- GCC_C_LANGUAGE_STANDARD = gnu99;
- GCC_DYNAMIC_NO_PIC = NO;
- GCC_NO_COMMON_BLOCKS = YES;
- GCC_OPTIMIZATION_LEVEL = 0;
- GCC_PREPROCESSOR_DEFINITIONS = (
- "DEBUG=1",
- "$(inherited)",
- );
- GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
- GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
- GCC_WARN_UNDECLARED_SELECTOR = YES;
- GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
- GCC_WARN_UNUSED_FUNCTION = YES;
- GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 10.0;
- MACOSX_DEPLOYMENT_TARGET = 10.10;
- MTL_ENABLE_DEBUG_INFO = YES;
- ONLY_ACTIVE_ARCH = YES;
- SDKROOT = macosx;
- SUPPORTED_PLATFORMS = "macosx iphoneos iphonesimulator";
- SWIFT_OPTIMIZATION_LEVEL = "-Onone";
- VERSIONING_SYSTEM = "apple-generic";
- VERSION_INFO_PREFIX = "";
- };
- name = Debug;
- };
- CBDA802F1B2D2C740010C653 /* Release */ = {
- isa = XCBuildConfiguration;
- buildSettings = {
- ALWAYS_SEARCH_USER_PATHS = NO;
- CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
- CLANG_CXX_LIBRARY = "libc++";
- CLANG_ENABLE_MODULES = YES;
- CLANG_ENABLE_OBJC_ARC = YES;
- CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
- CLANG_WARN_BOOL_CONVERSION = YES;
- CLANG_WARN_COMMA = YES;
- CLANG_WARN_CONSTANT_CONVERSION = YES;
- CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
- CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_EMPTY_BODY = YES;
- CLANG_WARN_ENUM_CONVERSION = YES;
- CLANG_WARN_INFINITE_RECURSION = YES;
- CLANG_WARN_INT_CONVERSION = YES;
- CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
- CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
- CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
- CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
- CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
- CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
- CLANG_WARN_STRICT_PROTOTYPES = YES;
- CLANG_WARN_SUSPICIOUS_MOVE = YES;
- CLANG_WARN_UNREACHABLE_CODE = YES;
- CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
- COPY_PHASE_STRIP = NO;
- CURRENT_PROJECT_VERSION = 1;
- DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
- ENABLE_NS_ASSERTIONS = NO;
- ENABLE_STRICT_OBJC_MSGSEND = YES;
- GCC_C_LANGUAGE_STANDARD = gnu99;
- GCC_NO_COMMON_BLOCKS = YES;
- GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
- GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
- GCC_WARN_UNDECLARED_SELECTOR = YES;
- GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
- GCC_WARN_UNUSED_FUNCTION = YES;
- GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 10.0;
- MACOSX_DEPLOYMENT_TARGET = 10.10;
- MTL_ENABLE_DEBUG_INFO = NO;
- ONLY_ACTIVE_ARCH = YES;
- SDKROOT = macosx;
- SUPPORTED_PLATFORMS = "macosx iphoneos iphonesimulator";
- SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
- VERSIONING_SYSTEM = "apple-generic";
- VERSION_INFO_PREFIX = "";
- };
- name = Release;
- };
- CBDA80311B2D2C740010C653 /* Debug */ = {
- isa = XCBuildConfiguration;
- buildSettings = {
- ALWAYS_SEARCH_USER_PATHS = NO;
- CLANG_ENABLE_MODULES = YES;
- CODE_SIGN_IDENTITY = "";
- COMBINE_HIDPI_IMAGES = YES;
- DEFINES_MODULE = YES;
- DEVELOPMENT_TEAM = "";
- DYLIB_COMPATIBILITY_VERSION = 1;
- DYLIB_CURRENT_VERSION = 1;
- DYLIB_INSTALL_NAME_BASE = "@rpath";
- GCC_GENERATE_TEST_COVERAGE_FILES = YES;
- INFOPLIST_FILE = "$(SRCROOT)/Sources/$(PROJECT_NAME)/Info.plist";
- INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
- LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
- MARKETING_VERSION = 2.2;
- PRODUCT_BUNDLE_IDENTIFIER = com.claysmith.HSLuvSwiftMac;
- PRODUCT_NAME = HSLuvSwift;
- SKIP_INSTALL = YES;
- SUPPORTED_PLATFORMS = macosx;
- SWIFT_OPTIMIZATION_LEVEL = "-Onone";
- SWIFT_SWIFT3_OBJC_INFERENCE = Default;
- SWIFT_VERSION = 5.0;
- };
- name = Debug;
- };
- CBDA80321B2D2C740010C653 /* Release */ = {
- isa = XCBuildConfiguration;
- buildSettings = {
- ALWAYS_SEARCH_USER_PATHS = NO;
- CLANG_ENABLE_MODULES = YES;
- CODE_SIGN_IDENTITY = "";
- COMBINE_HIDPI_IMAGES = YES;
- DEFINES_MODULE = YES;
- DEVELOPMENT_TEAM = "";
- DYLIB_COMPATIBILITY_VERSION = 1;
- DYLIB_CURRENT_VERSION = 1;
- DYLIB_INSTALL_NAME_BASE = "@rpath";
- GCC_GENERATE_TEST_COVERAGE_FILES = YES;
- INFOPLIST_FILE = "$(SRCROOT)/Sources/$(PROJECT_NAME)/Info.plist";
- INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
- LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
- MARKETING_VERSION = 2.2;
- PRODUCT_BUNDLE_IDENTIFIER = com.claysmith.HSLuvSwiftMac;
- PRODUCT_NAME = HSLuvSwift;
- SKIP_INSTALL = YES;
- SUPPORTED_PLATFORMS = macosx;
- SWIFT_SWIFT3_OBJC_INFERENCE = Default;
- SWIFT_VERSION = 5.0;
- };
- name = Release;
- };
-/* End XCBuildConfiguration section */
-
-/* Begin XCConfigurationList section */
- CB37B9161B2E7995009E63A5 /* Build configuration list for PBXNativeTarget "HSLuviOS" */ = {
- isa = XCConfigurationList;
- buildConfigurations = (
- CB37B9171B2E7995009E63A5 /* Debug */,
- CB37B9181B2E7995009E63A5 /* Release */,
- );
- defaultConfigurationIsVisible = 0;
- defaultConfigurationName = Release;
- };
- CB6AB2891B3083890075AF04 /* Build configuration list for PBXNativeTarget "HSLuvMacTests" */ = {
- isa = XCConfigurationList;
- buildConfigurations = (
- CB6AB2871B3083890075AF04 /* Debug */,
- CB6AB2881B3083890075AF04 /* Release */,
- );
- defaultConfigurationIsVisible = 0;
- defaultConfigurationName = Release;
- };
- CB6AB2A91B308A6F0075AF04 /* Build configuration list for PBXNativeTarget "HSLuviOSTests" */ = {
- isa = XCConfigurationList;
- buildConfigurations = (
- CB6AB2AA1B308A6F0075AF04 /* Debug */,
- CB6AB2AB1B308A6F0075AF04 /* Release */,
- );
- defaultConfigurationIsVisible = 0;
- defaultConfigurationName = Release;
- };
- CB98BFBC1B307B070027506A /* Build configuration list for PBXNativeTarget "HSLuvTests" */ = {
- isa = XCConfigurationList;
- buildConfigurations = (
- CB98BFBD1B307B070027506A /* Debug */,
- CB98BFBE1B307B070027506A /* Release */,
- );
- defaultConfigurationIsVisible = 0;
- defaultConfigurationName = Release;
- };
- CBDA80161B2D2C740010C653 /* Build configuration list for PBXProject "HSLuvSwift" */ = {
- isa = XCConfigurationList;
- buildConfigurations = (
- CBDA802E1B2D2C740010C653 /* Debug */,
- CBDA802F1B2D2C740010C653 /* Release */,
- );
- defaultConfigurationIsVisible = 0;
- defaultConfigurationName = Release;
- };
- CBDA80301B2D2C740010C653 /* Build configuration list for PBXNativeTarget "HSLuvMac" */ = {
- isa = XCConfigurationList;
- buildConfigurations = (
- CBDA80311B2D2C740010C653 /* Debug */,
- CBDA80321B2D2C740010C653 /* Release */,
- );
- defaultConfigurationIsVisible = 0;
- defaultConfigurationName = Release;
- };
-/* End XCConfigurationList section */
- };
- rootObject = CBDA80131B2D2C740010C653 /* Project object */;
-}
diff --git a/HSLuvSwift.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/HSLuvSwift.xcodeproj/project.xcworkspace/contents.xcworkspacedata
deleted file mode 100644
index 88159d8..0000000
--- a/HSLuvSwift.xcodeproj/project.xcworkspace/contents.xcworkspacedata
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
-
diff --git a/HSLuvSwift.xcodeproj/xcshareddata/xcschemes/HSLuvMac.xcscheme b/HSLuvSwift.xcodeproj/xcshareddata/xcschemes/HSLuvMac.xcscheme
deleted file mode 100644
index 3917b9b..0000000
--- a/HSLuvSwift.xcodeproj/xcshareddata/xcschemes/HSLuvMac.xcscheme
+++ /dev/null
@@ -1,96 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/HSLuvSwift.xcodeproj/xcshareddata/xcschemes/HSLuviOS.xcscheme b/HSLuvSwift.xcodeproj/xcshareddata/xcschemes/HSLuviOS.xcscheme
deleted file mode 100644
index 709a732..0000000
--- a/HSLuvSwift.xcodeproj/xcshareddata/xcschemes/HSLuviOS.xcscheme
+++ /dev/null
@@ -1,86 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/HSLuvSwift.xcworkspace/contents.xcworkspacedata b/HSLuvSwift.xcworkspace/contents.xcworkspacedata
deleted file mode 100644
index 6734618..0000000
--- a/HSLuvSwift.xcworkspace/contents.xcworkspacedata
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
-
-
-
-
diff --git a/HSLuvSwift.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/HSLuvSwift.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
deleted file mode 100644
index 18d9810..0000000
--- a/HSLuvSwift.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
- IDEDidComputeMac32BitWarning
-
-
-
diff --git a/HSLuviOS.playground/Contents.swift b/HSLuviOS.playground/Contents.swift
deleted file mode 100644
index f2b5d1d..0000000
--- a/HSLuviOS.playground/Contents.swift
+++ /dev/null
@@ -1,30 +0,0 @@
-//: # HSLuvSwift
-//:
-//: Swift port of HSLuv, a human-friendly alternative to HSL
-
-import UIKit
-import HSLuvSwift
-
-//: ## Examples
-//:
-//: The new UIColor initializer works exactly as you would expect.
-let color = UIColor(hue: 360, saturation: 100.0, lightness: 50.0, alpha: 1.0)
-let colorView = UIView(frame: squareRect)
-colorView.backgroundColor = color
-
-//: Alpha transparency is supported
-let fiftyPc = UIColor(hue: 200, saturation: 100, lightness: 50, alpha: 0.25)
-let fiftyPcView = UIView(frame: squareRect)
-fiftyPcView.backgroundColor = fiftyPc
-
-//: ## Let's make a rainbow!
-var colors = [UIColor?]()
-for H in stride(from: 0, through: 360, by: 5.0) {
- colors.append(UIColor(hue: H, saturation: 100.0, lightness: 50.0, alpha: 1.0))
-}
-
-if #available(iOS 9.0, *) {
- var palette = PaletteView(frame: CGRect(x: 0, y: 0, width: 500, height: 100), numberOfColors: 37)
-
- palette.addColors(colors)
-}
diff --git a/HSLuviOS.playground/Sources/Palette.swift b/HSLuviOS.playground/Sources/Palette.swift
deleted file mode 100644
index 1ef5e56..0000000
--- a/HSLuviOS.playground/Sources/Palette.swift
+++ /dev/null
@@ -1,44 +0,0 @@
-import UIKit
-
-public let squareRect = CGRect(x: 0, y: 0, width: 75, height: 75)
-
-@available(iOS 9.0, *)
-public class PaletteView: UIStackView {
- var subviewRect: CGRect!
-
- required public init(coder aDecoder: NSCoder) {
- fatalError()
- }
-
- public init(frame: CGRect, numberOfColors colors: Int) {
- super.init(frame: frame)
-
- if frame.width > frame.height {
- axis = .horizontal
- subviewRect = CGRect(x: 0, y: 0, width: frame.width / CGFloat(colors), height: frame.height)
- } else {
- axis = .vertical
- subviewRect = CGRect(x: 0, y: 0, width: frame.width, height: frame.height / CGFloat(colors))
- }
-
- distribution = .fillEqually
- }
-
- public func addColor(_ color: UIColor?) {
- guard let color = color else {
- print("Color is missing for view")
- return
- }
-
- let view = UIView(frame: subviewRect)
- view.backgroundColor = color
-
- self.addArrangedSubview(view)
- }
-
- public func addColors(_ colors: [UIColor?]) {
- for color in colors {
- addColor(color)
- }
- }
-}
diff --git a/HSLuviOS.playground/contents.xcplayground b/HSLuviOS.playground/contents.xcplayground
deleted file mode 100644
index a39f3b2..0000000
--- a/HSLuviOS.playground/contents.xcplayground
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/HSLuviOS.playground/timeline.xctimeline b/HSLuviOS.playground/timeline.xctimeline
deleted file mode 100644
index 94368c7..0000000
--- a/HSLuviOS.playground/timeline.xctimeline
+++ /dev/null
@@ -1,73 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/LICENSE b/LICENSE
index c6ef260..10a7ae7 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,34 +1,7 @@
-License
-=======
The MIT License (MIT)
-Original HSLuv implementation
-Copyright (c) 2015 Alexei Boronine
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-
---
-
-The MIT License (MIT)
-
-Swift implementation and supporting code
Copyright (c) 2015 Clay Smith
+Copyright (c) 2015, 2025 Alexei Boronine
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/Package.swift b/Package.swift
index 8b5b9ec..37dd0af 100644
--- a/Package.swift
+++ b/Package.swift
@@ -1,37 +1,17 @@
// swift-tools-version:5.3
-// The swift-tools-version declares the minimum version of Swift required to build this package.
-
import PackageDescription
let package = Package(
name: "HSLuvSwift",
- platforms: [
- .iOS(.v10),
- .macOS(.v10_11),
- ],
products: [
- // Products define the executables and libraries produced by a package, and make them visible to other packages.
- .library(
- name: "HSLuvSwift",
- targets: ["HSLuvSwift"]
- ),
- ],
- dependencies: [
- // Dependencies declare other packages that this package depends on.
- // .package(url: /* package url */, from: "1.0.0"),
+ .library(name: "HSLuv", targets: ["HSLuv"])
],
targets: [
- // Targets are the basic building blocks of a package. A target can define a module or a test suite.
- // Targets can depend on other targets in this package, and on products in packages which this package depends on.
- .target(
- name: "HSLuvSwift",
- dependencies: [],
- exclude: ["Info.plist"]
- ),
+ .target(name: "HSLuv", path: "Sources"),
.testTarget(
- name: "HSLuvSwiftTests",
- dependencies: ["HSLuvSwift"],
- exclude: ["Info.plist"],
+ name: "HSLuvTests",
+ dependencies: ["HSLuv"],
+ path: "Tests",
resources: [.copy("Resources/snapshot-rev4.json")],
swiftSettings: [.define("SPM")]
)
diff --git a/README.md b/README.md
index b4d40e9..19b4827 100644
--- a/README.md
+++ b/README.md
@@ -1,73 +1,70 @@
-# HSLuvSwift
+# HSLuv.swift
-[](https://swift.org/package-manager/)
-[](https://cocoapods.org/pods/HSLuvSwift)
-[](https://github.com/Carthage/Carthage)
[](https://github.com/hsluv/hsluv-swift/actions/workflows/ci.yml)
-[](LICENSE)
-Swift port of [HSLuv](http://www.hsluv.org) (revision 4), courtesy
-of [Clay Smith](https://github.com/stphnclysmth)
+Swift port of [HSLuv](http://www.hsluv.org) (revision 4). Supports Swift 5+ on Apple platforms, Linux and Windows.
-[Explanation, demo, ports etc.](http://www.hsluv.org)
+[Explanation, demo, etc.](http://www.hsluv.org)
+## INSTALL
-## USAGE
-
-This framework adds a single initializer on the OS-specific color class to create a color from HSLuv parameters. The initializer takes the same parameters on both macOS and iOS.
+Install HSLuv using [Swift Package Manager](https://www.swift.org/documentation/package-manager/) by adding the following to your [`Package.swift`](https://developer.apple.com/documentation/packagedescription):
```swift
-// macOS
-let color = NSColor(hue: 360.0, saturation: 100.0, lightness: 100.0, alpha: 1.0)
-
-// iOS
-let color = UIColor(hue: 360.0, saturation: 100.0, lightness: 100.0, alpha: 1.0)
+.package(url: "https://github.com/hsluv/hsluv-swift.git", from: "3.0.0"),
```
+CocoaPods and Carthage are no longer supported, but we recommend you simply vendor the [source file](./Sources/HSLuv.swift) if SPM is not available.
-## INSTALL
+## USAGE
-This project is compatible with Swift Package Manager, CocoaPods and Carthage. (These instructions assume that your chosen method is already installed.)
+The API is designed to avoid heap allocation. The `HSLuv` class defines the following public fields:
-#### Swift Package Manager
+- RGB: `hex:String`, `rgb_r:Float` [0;1], `rgb_g:Float` [0;1], `rgb_r:Float` [0;1]
+- CIE XYZ: `xyz_x:Float`, `xyz_y:Float`, `xyz_z:Float`
+- CIE LUV: `luv_l:Float`, `luv_u:Float`, `luv_v:Float`
+- CIE LUV LCh: `lch_l:Float`, `lch_c:Float`, `lch_h:Float`
+- HSLuv: `hsluv_h:Float` [0;360], `hsluv_s:Float` [0;100], `hsluv_l:Float` [0;100]
+- HPLuv: `hpluv_h:Float` [0;360], `hpluv_p:Float` [0;100], `hpluv_l:Float` [0;100]
-As of version 2.1.0, you can use the Swift Package Manager as integration method.
-If you want to use the Swift Package Manager as integration method, either use Xcode to add the package dependency or add the following dependency to your Package.swift:
+To convert between color spaces, simply set the properties of the source color space, run the
+conversion methods, then read the properties of the target color space.
-```swift
-.package(url: "https://github.com/hsluv/hsluv-swift.git", from: "2.1.0"),
-```
+Use the following methods to convert to and from RGB:
-### CocoaPods
+- HSLuv: `hsluvToRgb()`, `hsluvToHex()`, `rgbToHsluv()`, `hexToHsluv()`
+- HPLuv: `hpluvToRgb()`, `hpluvToHex()`, `rgbToHpluv()`, `hexToHpluv()`
-Add `pod 'HSLuvSwift'` to your target. Since this is a Swift dynamic framework, you must also tell CocoaPods to `use_frameworks!` instead of static libraries.
+Use the following methods to do step-by-step conversion:
-```ruby
-platform :ios, '10.0' # or, :osx, '10.10'
-use_frameworks!
+- Forward: `hsluvToLch()` (or `hpluvToLch()`), `lchToLuv()`, `luvToXyz()`, `xyzToRgb()`, `rgbToHex()`
+- Backward: `hexToRgb()`, `rgbToXyz()`, `xyzToLuv()`, `luvToLch()`, `lchToHsluv()` (or `lchToHpluv()`)
-target 'YourProject' do
-pod 'HSLuvSwift', '~> 2.0.0'
-end
-```
+For advanced usage, we also export the [bounding lines](https://www.hsluv.org/math/) in slope-intercept
+format, two for each RGB channel representing the limit of the gamut.
-### Carthage
+- R < 0: `r0s`, `r0i`
+- R > 1: `r1s`, `r1i`
+- G < 0: `g0s`, `g0i`
+- G > 1: `g1s`, `g1i`
+- B < 0: `b0s`, `b0i`
+- B > 1: `b1s`, `b1i`
-Add `github "hsluv/hsluv-swift" ~> 2.0.0` to your Cartfile and run `carthage bootstrap`. This builds frameworks for Mac and iOS targets.
+Example:
-```sh
-> echo 'github "hsluv/hsluv-swift" ~> 2.0.0' >> Cartfile
-> carthage bootstrap
+```swift
+let conv = Hsluv()
+conv.hsluv_h = 10
+conv.hsluv_s = 75
+conv.hsluv_l = 65
+conv.hsluvToHex()
+print(conv.hex) // Will print "#ec7d82"
```
+## CHANGELOG
-## TODO
-
-* Finish HPLuv implementation
-* Improve tests and add continuous integration testing
-* Add usage documentation
-
+See [CHANGELOG](CHANGELOG.md) for version history and release notes.
-## License
+## LICENSE
-See [License](LICENSE)
+See [LICENSE](LICENSE)
diff --git a/Sources/HSLuv.swift b/Sources/HSLuv.swift
new file mode 100644
index 0000000..cbd6454
--- /dev/null
+++ b/Sources/HSLuv.swift
@@ -0,0 +1,450 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2015 Clay Smith
+ * Copyright (c) 2015, 2025 Alexei Boronine
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+import Foundation
+
+public class Hsluv {
+ private static let hexChars: String = "0123456789abcdef"
+ private static let refY: Double = 1.0
+ private static let refU: Double = 0.19783000664283
+ private static let refV: Double = 0.46831999493879
+ private static let kappa: Double = 903.2962962
+ private static let epsilon: Double = 0.0088564516
+ private static let m_r0: Double = 3.240969941904521
+ private static let m_r1: Double = -1.537383177570093
+ private static let m_r2: Double = -0.498610760293
+ private static let m_g0: Double = -0.96924363628087
+ private static let m_g1: Double = 1.87596750150772
+ private static let m_g2: Double = 0.041555057407175
+ private static let m_b0: Double = 0.055630079696993
+ private static let m_b1: Double = -0.20397695888897
+ private static let m_b2: Double = 1.056971514242878
+
+ // RGB
+ public var hex: String = "#000000"
+ public var rgb_r: Double = 0
+ public var rgb_g: Double = 0
+ public var rgb_b: Double = 0
+
+ // CIE XYZ
+ public var xyz_x: Double = 0
+ public var xyz_y: Double = 0
+ public var xyz_z: Double = 0
+
+ // CIE LUV
+ public var luv_l: Double = 0
+ public var luv_u: Double = 0
+ public var luv_v: Double = 0
+
+ // CIE LUV LCh
+ public var lch_l: Double = 0
+ public var lch_c: Double = 0
+ public var lch_h: Double = 0
+
+ // HSLuv
+ public var hsluv_h: Double = 0
+ public var hsluv_s: Double = 0
+ public var hsluv_l: Double = 0
+
+ // HPLuv
+ public var hpluv_h: Double = 0
+ public var hpluv_p: Double = 0
+ public var hpluv_l: Double = 0
+
+ // 6 lines in slope-intercept format: R < 0, R > 1, G < 0, G > 1, B < 0, B > 1
+ public var r0s: Double = 0
+ public var r0i: Double = 0
+ public var r1s: Double = 0
+ public var r1i: Double = 0
+
+ public var g0s: Double = 0
+ public var g0i: Double = 0
+ public var g1s: Double = 0
+ public var g1i: Double = 0
+
+ public var b0s: Double = 0
+ public var b0i: Double = 0
+ public var b1s: Double = 0
+ public var b1i: Double = 0
+
+ public init() {}
+
+ private static func fromLinear(_ c: Double) -> Double {
+ if c <= 0.0031308 {
+ return 12.92 * c
+ } else {
+ return 1.055 * pow(c, 1 / 2.4) - 0.055
+ }
+ }
+
+ private static func toLinear(_ c: Double) -> Double {
+ if c > 0.04045 {
+ return pow((c + 0.055) / 1.055, 2.4)
+ } else {
+ return c / 12.92
+ }
+ }
+
+ private static func yToL(_ Y: Double) -> Double {
+ if Y <= epsilon {
+ return Y / refY * kappa
+ } else {
+ return 116 * pow(Y / refY, 1 / 3) - 16
+ }
+ }
+
+ private static func lToY(_ L: Double) -> Double {
+ if L <= 8 {
+ return refY * L / kappa
+ } else {
+ return refY * pow((L + 16) / 116, 3)
+ }
+ }
+
+ private static func rgbChannelToHex(_ chan: Double) -> String {
+ let c = Int(round(chan * 255))
+ let digit2 = c % 16
+ let digit1 = (c - digit2) / 16
+ let char1 = hexChars[hexChars.index(hexChars.startIndex, offsetBy: digit1)]
+ let char2 = hexChars[hexChars.index(hexChars.startIndex, offsetBy: digit2)]
+ return String(char1) + String(char2)
+ }
+
+ private static func hexToRgbChannel(_ hex: String, _ offset: Int) -> Double {
+ let char1 = hex[hex.index(hex.startIndex, offsetBy: offset)]
+ let char2 = hex[hex.index(hex.startIndex, offsetBy: offset + 1)]
+ let digit1 = hexChars.firstIndex(of: char1)!.utf16Offset(in: hexChars)
+ let digit2 = hexChars.firstIndex(of: char2)!.utf16Offset(in: hexChars)
+ let n = digit1 * 16 + digit2
+ return Double(n) / 255.0
+ }
+
+ private static func distanceFromOriginAngle(_ slope: Double, _ intercept: Double, _ angle: Double) -> Double {
+ let d = intercept / (sin(angle) - slope * cos(angle))
+ if d < 0 {
+ return Double.infinity
+ } else {
+ return d
+ }
+ }
+
+ private static func distanceFromOrigin(_ slope: Double, _ intercept: Double) -> Double {
+ return abs(intercept) / sqrt(pow(slope, 2) + 1)
+ }
+
+ private static func min6(_ f1: Double, _ f2: Double, _ f3: Double, _ f4: Double, _ f5: Double, _ f6: Double) -> Double {
+ return min(f1, min(f2, min(f3, min(f4, min(f5, f6)))))
+ }
+
+ public func rgbToHex() {
+ hex = "#"
+ hex += Hsluv.rgbChannelToHex(rgb_r)
+ hex += Hsluv.rgbChannelToHex(rgb_g)
+ hex += Hsluv.rgbChannelToHex(rgb_b)
+ }
+
+ public func hexToRgb() {
+ let lowercaseHex = hex.lowercased()
+ rgb_r = Hsluv.hexToRgbChannel(lowercaseHex, 1)
+ rgb_g = Hsluv.hexToRgbChannel(lowercaseHex, 3)
+ rgb_b = Hsluv.hexToRgbChannel(lowercaseHex, 5)
+ }
+
+ public func xyzToRgb() {
+ rgb_r = Hsluv.fromLinear(Hsluv.m_r0 * xyz_x + Hsluv.m_r1 * xyz_y + Hsluv.m_r2 * xyz_z)
+ rgb_g = Hsluv.fromLinear(Hsluv.m_g0 * xyz_x + Hsluv.m_g1 * xyz_y + Hsluv.m_g2 * xyz_z)
+ rgb_b = Hsluv.fromLinear(Hsluv.m_b0 * xyz_x + Hsluv.m_b1 * xyz_y + Hsluv.m_b2 * xyz_z)
+ }
+
+ public func rgbToXyz() {
+ let lr = Hsluv.toLinear(rgb_r)
+ let lg = Hsluv.toLinear(rgb_g)
+ let lb = Hsluv.toLinear(rgb_b)
+ xyz_x = 0.41239079926595 * lr + 0.35758433938387 * lg + 0.18048078840183 * lb
+ xyz_y = 0.21263900587151 * lr + 0.71516867876775 * lg + 0.072192315360733 * lb
+ xyz_z = 0.019330818715591 * lr + 0.11919477979462 * lg + 0.95053215224966 * lb
+ }
+
+ public func xyzToLuv() {
+ let divider = xyz_x + 15 * xyz_y + 3 * xyz_z
+ var varU = 4 * xyz_x
+ var varV = 9 * xyz_y
+ if divider != 0 {
+ varU /= divider
+ varV /= divider
+ } else {
+ varU = Double.nan
+ varV = Double.nan
+ }
+ luv_l = Hsluv.yToL(xyz_y)
+ if luv_l == 0 {
+ luv_u = 0
+ luv_v = 0
+ } else {
+ luv_u = 13 * luv_l * (varU - Hsluv.refU)
+ luv_v = 13 * luv_l * (varV - Hsluv.refV)
+ }
+ }
+
+ public func luvToXyz() {
+ if luv_l == 0 {
+ xyz_x = 0
+ xyz_y = 0
+ xyz_z = 0
+ return
+ }
+ let varU = luv_u / (13 * luv_l) + Hsluv.refU
+ let varV = luv_v / (13 * luv_l) + Hsluv.refV
+ xyz_y = Hsluv.lToY(luv_l)
+ xyz_x = 0 - 9 * xyz_y * varU / ((varU - 4) * varV - varU * varV)
+ xyz_z = (9 * xyz_y - 15 * varV * xyz_y - varV * xyz_x) / (3 * varV)
+ }
+
+ public func luvToLch() {
+ lch_l = luv_l
+ lch_c = sqrt(luv_u * luv_u + luv_v * luv_v)
+ if lch_c < 0.00000001 {
+ lch_h = 0
+ } else {
+ let hrad = atan2(luv_v, luv_u)
+ lch_h = hrad * 180.0 / Double.pi
+ if lch_h < 0 {
+ lch_h = 360 + lch_h
+ }
+ }
+ }
+
+ public func lchToLuv() {
+ let hrad = lch_h / 180.0 * Double.pi
+ luv_l = lch_l
+ luv_u = cos(hrad) * lch_c
+ luv_v = sin(hrad) * lch_c
+ }
+
+ public func calculateBoundingLines(_ l: Double) {
+ let sub1 = pow(l + 16, 3) / 1560896
+ let sub2 = sub1 > Hsluv.epsilon ? sub1 : l / Hsluv.kappa
+ let s1r = sub2 * (284517 * Hsluv.m_r0 - 94839 * Hsluv.m_r2)
+ let s2r = sub2 * (838422 * Hsluv.m_r2 + 769860 * Hsluv.m_r1 + 731718 * Hsluv.m_r0)
+ let s3r = sub2 * (632260 * Hsluv.m_r2 - 126452 * Hsluv.m_r1)
+ let s1g = sub2 * (284517 * Hsluv.m_g0 - 94839 * Hsluv.m_g2)
+ let s2g = sub2 * (838422 * Hsluv.m_g2 + 769860 * Hsluv.m_g1 + 731718 * Hsluv.m_g0)
+ let s3g = sub2 * (632260 * Hsluv.m_g2 - 126452 * Hsluv.m_g1)
+ let s1b = sub2 * (284517 * Hsluv.m_b0 - 94839 * Hsluv.m_b2)
+ let s2b = sub2 * (838422 * Hsluv.m_b2 + 769860 * Hsluv.m_b1 + 731718 * Hsluv.m_b0)
+ let s3b = sub2 * (632260 * Hsluv.m_b2 - 126452 * Hsluv.m_b1)
+ r0s = s1r / s3r
+ r0i = s2r * l / s3r
+ r1s = s1r / (s3r + 126452)
+ r1i = (s2r - 769860) * l / (s3r + 126452)
+ g0s = s1g / s3g
+ g0i = s2g * l / s3g
+ g1s = s1g / (s3g + 126452)
+ g1i = (s2g - 769860) * l / (s3g + 126452)
+ b0s = s1b / s3b
+ b0i = s2b * l / s3b
+ b1s = s1b / (s3b + 126452)
+ b1i = (s2b - 769860) * l / (s3b + 126452)
+ }
+
+ public func calcMaxChromaHpluv() -> Double {
+ let r0 = Hsluv.distanceFromOrigin(r0s, r0i)
+ let r1 = Hsluv.distanceFromOrigin(r1s, r1i)
+ let g0 = Hsluv.distanceFromOrigin(g0s, g0i)
+ let g1 = Hsluv.distanceFromOrigin(g1s, g1i)
+ let b0 = Hsluv.distanceFromOrigin(b0s, b0i)
+ let b1 = Hsluv.distanceFromOrigin(b1s, b1i)
+ return Hsluv.min6(r0, r1, g0, g1, b0, b1)
+ }
+
+ public func calcMaxChromaHsluv(_ h: Double) -> Double {
+ let hueRad = h / 360 * Double.pi * 2
+ let r0 = Hsluv.distanceFromOriginAngle(r0s, r0i, hueRad)
+ let r1 = Hsluv.distanceFromOriginAngle(r1s, r1i, hueRad)
+ let g0 = Hsluv.distanceFromOriginAngle(g0s, g0i, hueRad)
+ let g1 = Hsluv.distanceFromOriginAngle(g1s, g1i, hueRad)
+ let b0 = Hsluv.distanceFromOriginAngle(b0s, b0i, hueRad)
+ let b1 = Hsluv.distanceFromOriginAngle(b1s, b1i, hueRad)
+ return Hsluv.min6(r0, r1, g0, g1, b0, b1)
+ }
+
+ public func hsluvToLch() {
+ if hsluv_l > 99.9999999 {
+ lch_l = 100
+ lch_c = 0
+ } else if hsluv_l < 0.00000001 {
+ lch_l = 0
+ lch_c = 0
+ } else {
+ lch_l = hsluv_l
+ calculateBoundingLines(hsluv_l)
+ let max = calcMaxChromaHsluv(hsluv_h)
+ lch_c = max / 100 * hsluv_s
+ }
+ lch_h = hsluv_h
+ }
+
+ public func lchToHsluv() {
+ if lch_l > 99.9999999 {
+ hsluv_s = 0
+ hsluv_l = 100
+ } else if lch_l < 0.00000001 {
+ hsluv_s = 0
+ hsluv_l = 0
+ } else {
+ calculateBoundingLines(lch_l)
+ let max = calcMaxChromaHsluv(lch_h)
+ hsluv_s = lch_c / max * 100
+ hsluv_l = lch_l
+ }
+ hsluv_h = lch_h
+ }
+
+ public func hpluvToLch() {
+ if hpluv_l > 99.9999999 {
+ lch_l = 100
+ lch_c = 0
+ } else if hpluv_l < 0.00000001 {
+ lch_l = 0
+ lch_c = 0
+ } else {
+ lch_l = hpluv_l
+ calculateBoundingLines(hpluv_l)
+ let max = calcMaxChromaHpluv()
+ lch_c = max / 100 * hpluv_p
+ }
+ lch_h = hpluv_h
+ }
+
+ public func lchToHpluv() {
+ if lch_l > 99.9999999 {
+ hpluv_p = 0
+ hpluv_l = 100
+ } else if lch_l < 0.00000001 {
+ hpluv_p = 0
+ hpluv_l = 0
+ } else {
+ calculateBoundingLines(lch_l)
+ let max = calcMaxChromaHpluv()
+ hpluv_p = lch_c / max * 100
+ hpluv_l = lch_l
+ }
+ hpluv_h = lch_h
+ }
+
+ public func hsluvToRgb() {
+ hsluvToLch()
+ lchToLuv()
+ luvToXyz()
+ xyzToRgb()
+ }
+
+ public func hpluvToRgb() {
+ hpluvToLch()
+ lchToLuv()
+ luvToXyz()
+ xyzToRgb()
+ }
+
+ public func hsluvToHex() {
+ hsluvToRgb()
+ rgbToHex()
+ }
+
+ public func hpluvToHex() {
+ hpluvToRgb()
+ rgbToHex()
+ }
+
+ public func rgbToHsluv() {
+ rgbToXyz()
+ xyzToLuv()
+ luvToLch()
+ lchToHpluv()
+ lchToHsluv()
+ }
+
+ public func rgbToHpluv() {
+ rgbToXyz()
+ xyzToLuv()
+ luvToLch()
+ lchToHpluv()
+ }
+
+ public func hexToHsluv() {
+ hexToRgb()
+ rgbToHsluv()
+ }
+
+ public func hexToHpluv() {
+ hexToRgb()
+ rgbToHpluv()
+ }
+}
+
+/// Clamps a value between 0 and 1
+private func clamp(_ value: Double) -> Double {
+ return max(0.0, min(1.0, value))
+}
+
+/// Converts HSLuv color values to RGB tuple
+/// - Parameters:
+/// - h: Hue in degrees (0-360)
+/// - s: Saturation percentage (0-100)
+/// - l: Lightness percentage (0-100)
+/// - Returns: Named tuple with red, green, blue values in range 0-1
+public func hsluvToRgb(_ h: Double, _ s: Double, _ l: Double) -> (red: Double, green: Double, blue: Double) {
+ let converter = Hsluv()
+ converter.hsluv_h = h
+ converter.hsluv_s = s
+ converter.hsluv_l = l
+ converter.hsluvToRgb()
+
+ return (
+ red: clamp(converter.rgb_r),
+ green: clamp(converter.rgb_g),
+ blue: clamp(converter.rgb_b)
+ )
+}
+
+/// Converts HPLuv color values to RGB tuple
+/// - Parameters:
+/// - h: Hue in degrees (0-360)
+/// - p: Pastel percentage (0-100)
+/// - l: Lightness percentage (0-100)
+/// - Returns: Named tuple with red, green, blue values in range 0-1
+public func hpluvToRgb(_ h: Double, _ p: Double, _ l: Double) -> (red: Double, green: Double, blue: Double) {
+ let converter = Hsluv()
+ converter.hpluv_h = h
+ converter.hpluv_p = p
+ converter.hpluv_l = l
+ converter.hpluvToRgb()
+
+ return (
+ red: clamp(converter.rgb_r),
+ green: clamp(converter.rgb_g),
+ blue: clamp(converter.rgb_b)
+ )
+}
diff --git a/Sources/HSLuvSwift/Color+Extensions.swift b/Sources/HSLuvSwift/Color+Extensions.swift
deleted file mode 100644
index 3891993..0000000
--- a/Sources/HSLuvSwift/Color+Extensions.swift
+++ /dev/null
@@ -1,47 +0,0 @@
-//
-// The MIT License (MIT)
-//
-// Copyright (c) 2015 Clay Smith
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-
-#if os(iOS)
-import UIKit
-
-private typealias SystemColor = UIColor
-#elseif os(macOS)
-import AppKit
-
-private typealias SystemColor = NSColor
-#endif
-
-import CoreGraphics
-
-extension SystemColor: HSLuvInitializable, HSLuvConvertible {
- /// Convenience function to wrap the behavior of getRed(red:green:blue:alpha:)
- public func getRGB() -> (red: CGFloat, green: CGFloat, blue: CGFloat) {
- var red: CGFloat = 0, green: CGFloat = 0, blue: CGFloat = 0, alpha: CGFloat = 0
- getRed(&red, green: &green, blue: &blue, alpha: &alpha)
- return (red, green, blue)
- }
-
-}
-
-extension SystemColor: HPLuvInitializable, HPLuvConvertible {}
diff --git a/Sources/HSLuvSwift/ColorEncodings.swift b/Sources/HSLuvSwift/ColorEncodings.swift
deleted file mode 100644
index cc7d003..0000000
--- a/Sources/HSLuvSwift/ColorEncodings.swift
+++ /dev/null
@@ -1,137 +0,0 @@
-//
-// The MIT License (MIT)
-//
-// Copyright (c) 2015 Clay Smith
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-
-// Using structs instead of tuples prevents implicit conversion,
-// which was making debugging difficult
-
-import Foundation
-
-typealias Tuple = (Double, Double, Double)
-
-protocol TupleConverter {
- var tuple: Tuple { get }
-}
-
-/// Hexadecimal color
-struct Hex {
- let string: String
-
- init(_ string: String) {
- self.string = string
- }
-}
-
-/// Red, Green, Blue (RGB)
-struct RGBTuple: TupleConverter {
- var R: Double
- var G: Double
- var B: Double
-
- init(_ R: Double, _ G: Double, _ B: Double) {
- self.R = R
- self.G = G
- self.B = B
- }
-
- var tuple: Tuple {
- (R, G, B)
- }
-}
-
-/// Luminance, Blue-stimulation, Cone-response [CIE 1931] (XYZ)
-struct XYZTuple: TupleConverter {
- var X: Double
- var Y: Double
- var Z: Double
-
- init(_ X: Double, _ Y: Double, _ Z: Double) {
- self.X = X
- self.Y = Y
- self.Z = Z
- }
-
- var tuple: Tuple {
- (X, Y, Z)
- }
-}
-
-/// L*, u*, v* [CIE 1976] (LUV)
-struct LUVTuple {
- var L: Double
- var U: Double
- var V: Double
-
- init(_ L: Double, _ U: Double, _ V: Double) {
- self.L = L
- self.U = U
- self.V = V
- }
-}
-
-/// Lightness, Chroma, Hue (LCH)
-struct LCHTuple {
- var L: Double
- var C: Double
- var H: Double
-
- init(_ L: Double, _ C: Double, _ H: Double) {
- self.L = L
- self.C = C
- self.H = H
- }
-}
-
-/// Hue(man), Saturation, Lightness (HSLuv)
-struct HSLuvTuple: TupleConverter {
- var H: Double
- var S: Double
- var L: Double
-
- init(_ H: Double, _ S: Double, _ L: Double) {
- self.H = H
- self.S = S
- self.L = L
- }
-
- var tuple: Tuple {
- (H, S, L)
- }
-}
-
-/// Hue(man), Pastel saturation, Lightness (HPLuv)
-struct HPLuvTuple: TupleConverter {
- var H: Double
- var P: Double
- var L: Double
-
- init(_ H: Double, _ P: Double, _ L: Double) {
- self.H = H
- self.P = P
- self.L = L
- }
-
- var tuple: Tuple {
- (H, P, L)
- }
-}
diff --git a/Sources/HSLuvSwift/Constant.swift b/Sources/HSLuvSwift/Constant.swift
deleted file mode 100644
index ec6527a..0000000
--- a/Sources/HSLuvSwift/Constant.swift
+++ /dev/null
@@ -1,50 +0,0 @@
-//
-// The MIT License (MIT)
-//
-// Copyright (c) 2015 Alexei Boronine
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-
-import Foundation
-
-struct Constant {
- static var m = (
- R: Tuple(3.2409699419045214, -1.5373831775700935, -0.49861076029300328),
- G: Tuple(-0.96924363628087983, 1.8759675015077207, 0.041555057407175613),
- B: Tuple(0.055630079696993609, -0.20397695888897657, 1.0569715142428786)
- )
-
- static var mInv = (
- X: Tuple(0.41239079926595948, 0.35758433938387796, 0.18048078840183429),
- Y: Tuple(0.21263900587151036, 0.71516867876775593, 0.072192315360733715),
- Z: Tuple(0.019330818715591851, 0.11919477979462599, 0.95053215224966058)
- )
-
- // Hard-coded D65 standard illuminant
- static var refU = 0.19783000664283681
- static var refV = 0.468319994938791
-
- // CIE LUV constants
- static var kappa = 903.2962962962963
- static var epsilon = 0.0088564516790356308
-
- // Swift limitations
- static var maxDouble = Double.greatestFiniteMagnitude
-}
diff --git a/Sources/HSLuvSwift/Info.plist b/Sources/HSLuvSwift/Info.plist
deleted file mode 100644
index ca23c84..0000000
--- a/Sources/HSLuvSwift/Info.plist
+++ /dev/null
@@ -1,26 +0,0 @@
-
-
-
-
- CFBundleDevelopmentRegion
- en
- CFBundleExecutable
- $(EXECUTABLE_NAME)
- CFBundleIdentifier
- $(PRODUCT_BUNDLE_IDENTIFIER)
- CFBundleInfoDictionaryVersion
- 6.0
- CFBundleName
- $(PRODUCT_NAME)
- CFBundlePackageType
- FMWK
- CFBundleShortVersionString
- $(MARKETING_VERSION)
- CFBundleSignature
- ????
- CFBundleVersion
- $(CURRENT_PROJECT_VERSION)
- NSPrincipalClass
-
-
-
diff --git a/Sources/HSLuvSwift/Math.swift b/Sources/HSLuvSwift/Math.swift
deleted file mode 100644
index 1a96841..0000000
--- a/Sources/HSLuvSwift/Math.swift
+++ /dev/null
@@ -1,369 +0,0 @@
-//
-// The MIT License (MIT)
-//
-// Copyright (c) 2015 Alexei Boronine
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-
-import Foundation
-
-// MARK: - Vector math
-typealias Vector = (Double, Double)
-
-/// For a given lightness, return a list of 6 lines in slope-intercept
-/// form that represent the bounds in CIELUV, stepping over which will
-/// push a value out of the RGB gamut
-///
-/// - parameter lightness: Double
-func getBounds(lightness L: Double) -> [Vector] {
- let sub1: Double = pow(L + 16, 3) / 1560896
- let sub2 = sub1 > Constant.epsilon ? sub1 : L / Constant.kappa
-
- var result = [Vector]()
-
- let mirror = Mirror(reflecting: Constant.m)
- for (_, value) in mirror.children {
- let (m1, m2, m3) = value as! Tuple
-
- for t in [0.0, 1.0] {
- let top1 = (284517 * m1 - 94839 * m3) * sub2
- let top2 = (838422 * m3 + 769860 * m2 + 731718 * m1) * L * sub2 - 769860 * t * L
- let bottom = (632260 * m3 - 126452 * m2) * sub2 + 126452 * t
-
- result.append((top1 / bottom, top2 / bottom))
- }
- }
-
- return result
-}
-
-func intersectLine(_ line1: Vector, _ line2: Vector) -> Double {
- return (line1.1 - line2.1) / (line2.0 - line1.0)
-}
-
-func distanceFromPole(_ point: Vector) -> Double {
- return sqrt(pow(point.0, 2) + pow(point.1, 2))
-}
-
-func lengthOfRayUntilIntersect(theta: Double, line: Vector) -> Double? {
- // theta -- angle of ray starting at (0, 0)
- // m, b -- slope and intercept of line
- // x1, y1 -- coordinates of intersection
- // len -- length of ray until it intersects with line
- //
- // b + m * x1 = y1
- // len >= 0
- // len * cos(theta) = x1
- // len * sin(theta) = y1
- //
- //
- // b + m * (len * cos(theta)) = len * sin(theta)
- // b = len * sin(hrad) - m * len * cos(theta)
- // b = len * (sin(hrad) - m * cos(hrad))
- // len = b / (sin(hrad) - m * cos(hrad))
-
- let (m1, b1) = line
- let len = b1 / (sin(theta) - m1 * cos(theta))
-
- if len < 0 {
- return nil
- }
-
- return len
-}
-
-// MARK: RGB methods
-
-/// For given lightness, returns the maximum chroma. Keeping the chroma value
-/// below this number will ensure that for any hue, the color is within the RGB
-/// gamut.
-func maxChroma(lightness L: Double) -> Double {
- var lengths = [Double]()
-
- for (m1, b1) in getBounds(lightness: L) {
- // x where line intersects with perpendicular running though (0, 0)
- let x = intersectLine((m1, b1), (-1 / m1, 0))
- lengths.append(distanceFromPole((x, b1 + x * m1)))
- }
-
- return lengths.reduce(Constant.maxDouble) { min($0, $1) }
-}
-
-/// For a given lightness and hue, return the maximum chroma that fits in
-/// the RGB gamut.
-func maxChroma(lightness L: Double, hue H: Double) -> Double {
- let hrad = H / 360 * Double.pi * 2
-
- var lengths = [Double]()
- for line in getBounds(lightness: L) {
- if let l = lengthOfRayUntilIntersect(theta: hrad, line: line) {
- lengths.append(l)
- }
- }
-
- return lengths.reduce(Constant.maxDouble) { min($0, $1) }
-}
-
-func dotProduct(_ a: Tuple, b: T) -> Double {
- let b = b.tuple
-
- var ret = 0.0
-
- ret += a.0 * b.0
- ret += a.1 * b.1
- ret += a.2 * b.2
-
- return ret
-}
-
-// Used for RGB conversions
-func fromLinear(_ c: Double) -> Double {
- if c <= 0.0031308 {
- return 12.92 * c
- }
-
- return 1.055 * pow(c, 1 / 2.4) - 0.055
-}
-
-func toLinear(_ c: Double) -> Double {
- let a = 0.055
- if c > 0.04045 {
- return pow((c + a) / (1 + a), 2.4)
- }
-
- return c / 12.92
-}
-
-// MARK: - CIELUVTuple
-
-// In these formulas, Yn refers to the reference white point. We are using
-// illuminant D65, so Yn (see refY in Maxima file) equals 1. The formula is
-// simplified accordingly.
-
-func yToL(_ Y: Double) -> Double {
- if Y <= Constant.epsilon {
- return Y * Constant.kappa
- }
-
- return 116 * pow(Y, 1/3) - 16
-}
-
-func lToY(_ L: Double) -> Double {
- if L <= 8 {
- return L / Constant.kappa
- }
-
- return pow((L + 16) / 116, 3)
-}
-
-// MARK: - XYZ/RGB Conversion
-
-func xyzToRgb(_ xyz: XYZTuple) -> RGBTuple {
- let R = fromLinear(dotProduct(Constant.m.R, b: xyz))
- let G = fromLinear(dotProduct(Constant.m.G, b: xyz))
- let B = fromLinear(dotProduct(Constant.m.B, b: xyz))
-
- return RGBTuple(R, G, B)
-}
-
-func rgbToXyz(_ rgb: RGBTuple) -> XYZTuple {
- let rgbl = RGBTuple(toLinear(rgb.R), toLinear(rgb.G), toLinear(rgb.B))
-
- let X = dotProduct(Constant.mInv.X, b: rgbl)
- let Y = dotProduct(Constant.mInv.Y, b: rgbl)
- let Z = dotProduct(Constant.mInv.Z, b: rgbl)
-
- return XYZTuple(X, Y, Z)
-}
-
-// MARK: - XYZ/LUV Conversion
-
-func xyzToLuv(_ xyz: XYZTuple) -> LUVTuple {
- let varU = (4 * xyz.X) / (xyz.X + (15 * xyz.Y) + (3 * xyz.Z))
- let varV = (9 * xyz.Y) / (xyz.X + (15 * xyz.Y) + (3 * xyz.Z))
-
- let L = yToL(xyz.Y)
-
- guard L != 0 else {
- // Black will create a divide-by-zero error
- return LUVTuple(0, 0, 0)
- }
-
- let U = 13 * L * (varU - Constant.refU)
- let V = 13 * L * (varV - Constant.refV)
-
- return LUVTuple(L, U, V)
-}
-
-func luvToXyz(_ luv: LUVTuple) -> XYZTuple {
- guard luv.L != 0 else {
- // Black will create a divide-by-zero error
- return XYZTuple(0, 0, 0)
- }
-
- let varU = luv.U / (13 * luv.L) + Constant.refU
- let varV = luv.V / (13 * luv.L) + Constant.refV
-
- let Y = lToY(luv.L)
- let X = 0 - (9 * Y * varU) / ((varU - 4) * varV - varU * varV)
- let Z = (9 * Y - (15 * varV * Y) - (varV * X)) / (3 * varV)
-
- return XYZTuple(X, Y, Z)
-}
-
-// MARK: - LUV/LCH Conversion
-
-func luvToLch(_ luv: LUVTuple) -> LCHTuple {
- let C = sqrt(pow(luv.U, 2) + pow(luv.V, 2))
-
- guard C >= 0.00000001 else {
- // Greys: disambiguate hue
- return LCHTuple(luv.L, C, 0)
- }
-
- let Hrad = atan2(luv.V, luv.U)
- var H = Hrad * 360 / 2 / Double.pi
-
- if H < 0 {
- H = 360 + H
- }
-
- return LCHTuple(luv.L, C, H)
-}
-
-func lchToLuv(_ lch: LCHTuple) -> LUVTuple {
- let Hrad = lch.H / 360 * 2 * Double.pi
- let U = cos(Hrad) * lch.C
- let V = sin(Hrad) * lch.C
-
- return LUVTuple(lch.L, U, V)
-}
-
-// MARK: - HSLuv/LCH Conversion
-
-func hsluvToLch(_ hsluv: HSLuvTuple) -> LCHTuple {
- guard hsluv.L <= 99.9999999 && hsluv.L >= 0.00000001 else {
- // White and black: disambiguate chroma
- return LCHTuple(hsluv.L, 0, hsluv.H)
- }
-
- let max = maxChroma(lightness: hsluv.L, hue: hsluv.H)
- let C = max / 100 * hsluv.S
-
- return LCHTuple(hsluv.L, C, hsluv.H)
-}
-
-func lchToHsluv(_ lch: LCHTuple) -> HSLuvTuple {
- guard lch.L <= 99.9999999 && lch.L >= 0.00000001 else {
- // White and black: disambiguate saturation
- return HSLuvTuple(lch.H, 0, lch.L)
- }
-
- let max = maxChroma(lightness: lch.L, hue: lch.H)
- let S = lch.C / max * 100
-
- return HSLuvTuple(lch.H, S, lch.L)
-}
-
-// MARK: - Pastel HSLuv/LCH Conversion
-
-func hpluvToLch(_ hsluv: HSLuvTuple) -> LCHTuple {
- guard hsluv.L <= 99.9999999 && hsluv.L >= 0.00000001 else {
- // White and black: disambiguate chroma
- return LCHTuple(hsluv.L, 0, hsluv.H)
- }
-
- let max = maxChroma(lightness: hsluv.L)
- let C = max / 100 * hsluv.S
-
- return LCHTuple(hsluv.L, C, hsluv.H)
-}
-
-func lchToHpluv(_ lch: LCHTuple) -> HPLuvTuple {
- guard lch.L <= 99.9999999 && lch.L >= 0.00000001 else {
- // White and black: disambiguate saturation
- return HPLuvTuple(lch.H, 0, lch.L)
- }
-
- let max = maxChroma(lightness: lch.L)
- let S = lch.C / max * 100
-
- return HPLuvTuple(lch.H, S, lch.L)
-}
-
-// MARK: - RGB/Hex Conversion
-func round(_ value: Double, places: Double) -> Double {
- let divisor = pow(10.0, places)
- return round(value * divisor) / divisor
-}
-
-func getHexString(_ channel: Double) -> String {
- var ch = round(channel, places: 6)
-
- if ch < 0 || ch > 1 {
- // TODO: Implement Swift thrown errors
- fatalError("Illegal RGB value: \(ch)")
- }
-
- ch = round(ch * 255.0)
-
- return String(Int(ch), radix: 16, uppercase: false).padding(toLength: 2, withPad: "0", startingAt: 0)
-}
-
-func rgbToHex(_ rgb: RGBTuple) -> Hex {
- let R = getHexString(rgb.R)
- let G = getHexString(rgb.G)
- let B = getHexString(rgb.B)
-
- return Hex("#\(R)\(G)\(B)")
-}
-
-// This function is based on a comment by mehawk on gist arshad/de147c42d7b3063ef7bc.
-// It is so flippin' elegant.
-func hexToRgb(_ hex: Hex) -> RGBTuple {
- let string = hex.string.replacingOccurrences(of: "#", with: "")
-
- var rgbValue: UInt32 = 0
- Scanner(string: string).scanHexInt32(&rgbValue)
-
- return RGBTuple(
- Double((rgbValue & 0xFF0000) >> 16) / 255.0,
- Double((rgbValue & 0x00FF00) >> 8) / 255.0,
- Double( rgbValue & 0x0000FF) / 255.0
- )
-}
-
-// MARK: - Conversion shortcuts
-
-func hsluvToRgb(_ hsl: HSLuvTuple) -> RGBTuple {
- return xyzToRgb(luvToXyz(lchToLuv(hsluvToLch(hsl))))
-}
-
-func rgbToHsluv(_ rgb: RGBTuple) -> HSLuvTuple {
- return lchToHsluv(luvToLch(xyzToLuv(rgbToXyz(rgb))))
-}
-
-func hpluvToRgb(_ hsl: HSLuvTuple) -> RGBTuple {
- return xyzToRgb(luvToXyz(lchToLuv(hpluvToLch(hsl))))
-}
-
-func rgbToHpluv(_ rgb: RGBTuple) -> HPLuvTuple {
- return lchToHpluv(luvToLch(xyzToLuv(rgbToXyz(rgb))))
-}
diff --git a/Sources/HSLuvSwift/Protocols.swift b/Sources/HSLuvSwift/Protocols.swift
deleted file mode 100644
index 1a4ea55..0000000
--- a/Sources/HSLuvSwift/Protocols.swift
+++ /dev/null
@@ -1,118 +0,0 @@
-//
-// The MIT License (MIT)
-//
-// Copyright (c) 2015 Alexei Boronine
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-
-import CoreGraphics
-
-// Abstracts NSColor / UIColor
-public protocol Color {
-
- init(red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat)
-
- /// Convenience function to wrap the behavior of getRed(red:green:blue:alpha:)
- func getRGB() -> (red: CGFloat, green: CGFloat, blue: CGFloat)
-
-}
-
-extension Color {
-
- func getRGBTuple() -> RGBTuple {
- let (red, green, blue) = getRGB()
- return RGBTuple(Double(red), Double(green), Double(blue))
- }
-
-}
-
-// MARK: - Initialization protocols
-
-public protocol HSLuvInitializable: Color {}
-extension HSLuvInitializable {
-
- /// Initializes and returns a color object using the specified opacity and
- /// HSLuv color space component values.
- ///
- /// - parameter hue: Double
- /// - parameter saturation: Double
- /// - parameter lightness: Double
- /// - parameter alpha: Double
- public init(hue: Double, saturation: Double, lightness: Double, alpha: Double) {
- let rgb = hsluvToRgb(HSLuvTuple(hue, saturation, lightness))
- self.init(red: CGFloat(rgb.R), green: CGFloat(rgb.G), blue: CGFloat(rgb.B), alpha: CGFloat(alpha))
- }
-
-}
-
-public protocol HPLuvInitializable: Color {}
-extension HPLuvInitializable {
-
- /// Initializes and returns a color object using the specified opacity and
- /// HPLuv color space component values.
- ///
- /// - parameter hue: Double
- /// - parameter pastelSaturation: Double
- /// - parameter lightness: Double
- /// - parameter alpha: Double
- public init(hue: Double, pastelSaturation: Double, lightness: Double, alpha: Double) {
- let rgb = hpluvToRgb(HSLuvTuple(hue, pastelSaturation, lightness))
- self.init(red: CGFloat(rgb.R), green: CGFloat(rgb.G), blue: CGFloat(rgb.B), alpha: CGFloat(alpha))
- }
-
-}
-
-// MARK: - To HSLuv HPLuv conversions
-
-extension HSLuvTuple {
-
- init(_ color: Color) {
- self = rgbToHsluv(color.getRGBTuple())
- }
-
-}
-
-extension HPLuvTuple {
-
- init(_ color: Color) {
- self = rgbToHpluv(color.getRGBTuple())
- }
-
-}
-
-public protocol HSLuvConvertible: Color {}
-extension HSLuvConvertible {
-
- /// Returns the HSLuv color space component values for this color.
- public func getHSLuv() -> (hue: Double, saturation: Double, lightness: Double) {
- HSLuvTuple(self).tuple
- }
-
-}
-
-public protocol HPLuvConvertible: Color {}
-extension HPLuvConvertible {
-
- /// Returns the HPLuv color space component values for this color.
- public func getHPLuv() -> (hue: Double, pastelSaturation: Double, lightness: Double) {
- HPLuvTuple(self).tuple
- }
-
-}
diff --git a/Tests/HSLuvSwiftTests/HSLuvTests.swift b/Tests/HSLuvSwiftTests/HSLuvTests.swift
deleted file mode 100644
index fabbb34..0000000
--- a/Tests/HSLuvSwiftTests/HSLuvTests.swift
+++ /dev/null
@@ -1,83 +0,0 @@
-//
-// Common.swift
-// HSLuvSwift
-//
-// Created by Clay Smith on 6/16/15.
-// Copyright © 2015 Clay Smith. All rights reserved.
-//
-
-import Foundation
-import XCTest
-@testable import HSLuvSwift
-
-// TODO: Add HPLuv tests
-
-class HSLuvTests: XCTestCase {
- let rgbRangeTolerance = 0.000000001
- let snapshotTolerance = 0.000000001
-
- override func setUp() {
- super.setUp()
- // Put setup code here. This method is called before the invocation of each test method in the class.
-
- continueAfterFailure = false
- }
-
- func testConversionConsistency() {
- for fromHex in Snapshot.hexSamples {
- let fromRgb = hexToRgb(Hex(fromHex))
- let fromXyz = rgbToXyz(fromRgb)
- let fromLuv = xyzToLuv(fromXyz)
- let fromLch = luvToLch(fromLuv)
- let fromHsluv = lchToHsluv(fromLch)
-
- let toLch = hsluvToLch(fromHsluv)
- let toLuv = lchToLuv(toLch)
- let toXyz = luvToXyz(toLuv)
- let toRgb = xyzToRgb(toXyz)
- let toHex = rgbToHex(toRgb)
-
- XCTAssertEqual(fromLch.L, toLch.L, accuracy: rgbRangeTolerance)
- XCTAssertEqual(fromLch.C, toLch.C, accuracy: rgbRangeTolerance)
- XCTAssertEqual(fromLch.H, toLch.H, accuracy: rgbRangeTolerance)
-
- XCTAssertEqual(fromLuv.L, toLuv.L, accuracy: rgbRangeTolerance)
- XCTAssertEqual(fromLuv.U, toLuv.U, accuracy: rgbRangeTolerance)
- XCTAssertEqual(fromLuv.V, toLuv.V, accuracy: rgbRangeTolerance)
-
- XCTAssertEqual(fromXyz.X, toXyz.X, accuracy: rgbRangeTolerance)
- XCTAssertEqual(fromXyz.Y, toXyz.Y, accuracy: rgbRangeTolerance)
- XCTAssertEqual(fromXyz.Z, toXyz.Z, accuracy: rgbRangeTolerance)
-
- XCTAssertEqual(fromRgb.R, toRgb.R, accuracy: rgbRangeTolerance)
- XCTAssertEqual(fromRgb.G, toRgb.G, accuracy: rgbRangeTolerance)
- XCTAssertEqual(fromRgb.B, toRgb.B, accuracy: rgbRangeTolerance)
-
- XCTAssertEqual(fromHex, toHex.string)
- }
- }
-
- func testRgbRangeTolerance() {
- for h in stride(from: 0.0, through: 360, by: 5) {
- for s in stride(from: 0.0, through: 100, by: 5) {
- for l in stride(from: 0.0, through: 100, by: 5) {
- let tRgb = hsluvToRgb(HSLuvTuple(h, s, l))
- let rgb = [tRgb.R, tRgb.G, tRgb.B]
-
- for channel in rgb {
- XCTAssertGreaterThan(channel, -rgbRangeTolerance, "HSLuv: \([h, s, l]) -> RGB: \(rgb)")
- XCTAssertLessThanOrEqual(channel, 1 + rgbRangeTolerance, "HSLuv: \([h, s, l]) -> RGB: \(rgb)")
- }
- }
- }
- }
- }
-
- func testSnapshot() {
- Snapshot.compare(Snapshot.current) { [snapshotTolerance] hex, tag, stableTuple, currentTuple, stableChannel, currentChannel in
- let diff = abs(currentChannel - stableChannel)
-
- XCTAssertLessThan(diff, snapshotTolerance, "Snapshots for \(hex) don't match at \(tag): (stable: \(stableTuple), current: \(currentTuple)")
- }
- }
-}
diff --git a/Tests/HSLuvSwiftTests/Info.plist b/Tests/HSLuvSwiftTests/Info.plist
deleted file mode 100644
index ba72822..0000000
--- a/Tests/HSLuvSwiftTests/Info.plist
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
- CFBundleDevelopmentRegion
- en
- CFBundleExecutable
- $(EXECUTABLE_NAME)
- CFBundleIdentifier
- $(PRODUCT_BUNDLE_IDENTIFIER)
- CFBundleInfoDictionaryVersion
- 6.0
- CFBundleName
- $(PRODUCT_NAME)
- CFBundlePackageType
- BNDL
- CFBundleShortVersionString
- 1.0
- CFBundleSignature
- ????
- CFBundleVersion
- 1
-
-
diff --git a/Tests/HSLuvSwiftTests/MacExtensions/AppKit.swift b/Tests/HSLuvSwiftTests/MacExtensions/AppKit.swift
deleted file mode 100644
index 67cca20..0000000
--- a/Tests/HSLuvSwiftTests/MacExtensions/AppKit.swift
+++ /dev/null
@@ -1,55 +0,0 @@
-//
-// The MIT License (MIT)
-//
-// Copyright (c) 2015 Clay Smith
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-
-import Foundation
-import XCTest
-import HSLuvSwift
-
-#if canImport(AppKit)
-
-class AppKitTests: XCTestCase {
- let rgbRangeTolerance = 0.00000000001
-
- func testNSColorRGBRangeTolerance() {
- for h in stride(from: 0.0, through: 360, by: 5) {
- for s in stride(from: 0.0, through: 100, by: 5) {
- for l in stride(from: 0.0, through: 100, by: 5) {
- let color = NSColor(hue: h, saturation: s, lightness: l, alpha: 1.0)
-
- XCTAssertNotNil(color)
-
- let tRgb = color.getRGB()
- let rgb = [tRgb.red, tRgb.green, tRgb.blue].map { Double($0) }
-
- for channel in rgb {
- XCTAssertGreaterThan(channel, -rgbRangeTolerance, "HSLuv: \([h, s, l]) -> RGB: \(rgb)")
- XCTAssertLessThanOrEqual(channel, 1 + rgbRangeTolerance, "HSLuv: \([h, s, l]) -> RGB: \(rgb)")
- }
- }
- }
- }
- }
-}
-
-#endif
diff --git a/Tests/HSLuvSwiftTests/Snapshot.swift b/Tests/HSLuvSwiftTests/Snapshot.swift
deleted file mode 100644
index 1defd74..0000000
--- a/Tests/HSLuvSwiftTests/Snapshot.swift
+++ /dev/null
@@ -1,103 +0,0 @@
-//
-// Snapshot.swift
-// HSLuvSwift
-//
-// Created by Clay Smith on 6/17/15.
-// Copyright © 2015 Clay Smith. All rights reserved.
-//
-
-import Foundation
-import XCTest
-@testable import HSLuvSwift
-
-// TODO: Add HPLuv support
-
-typealias SnapshotType = [String: [String: [Double]]]
-
-class Snapshot {
- static var hexSamples: [String] = {
- let samples = "0123456789abcdef"
-
- var hexSamples = [String]()
- samples.forEach { r in
- samples.forEach { g in
- samples.forEach { b in
- hexSamples.append("#\(r)\(r)\(g)\(g)\(b)\(b)")
- }
- }
- }
-
- return hexSamples
- }()
-
- static var stable: SnapshotType = {
- // SPM has a special way of accessing bundle resources,
- // therefore we need to handle this differenlty than in the project.
- #if SPM
- guard let url = Bundle.module.url(forResource: "snapshot-rev4", withExtension: "json") else {
- fatalError("Snapshot JSON file is missing")
- }
- #else
- guard let url = Bundle(for: Snapshot.self).url(forResource: "snapshot-rev4", withExtension: "json") else {
- fatalError("Snapshot JSON file is missing")
- }
- #endif
-
- let jsonData = try! Data(contentsOf: url, options: .mappedIfSafe)
- let jsonResult = try! JSONSerialization.jsonObject(with: jsonData, options: .mutableContainers) as! SnapshotType
-
- return jsonResult
- }()
-
- static var current: SnapshotType = {
- var current = SnapshotType()
-
- for sample in Snapshot.hexSamples {
- let hex = Hex(sample)
-
- let rgb = hexToRgb(hex)
- let xyz = rgbToXyz(rgb)
- let luv = xyzToLuv(xyz)
- let lch = luvToLch(luv)
- let hsluv = lchToHsluv(lch)
- let hpluv = lchToHpluv(lch)
-
- current[sample] = [
- "rgb": [rgb.R, rgb.G, rgb.B],
- "xyz": [xyz.X, xyz.Y, xyz.Z],
- "luv": [luv.L, luv.U, luv.V],
- "lch": [lch.L, lch.C, lch.H],
- "hsluv": [hsluv.H, hsluv.S, hsluv.L],
- "hpluv": [hpluv.H, hpluv.P, hpluv.L]
- ]
- }
-
- return current
- }()
-
- static func compare(_ snapshot: SnapshotType,
- block: (_ hex: String, _ tag: String, _ stableTuple: [Double], _ currentTuple: [Double], _ stableChannel: Double, _ currentChannel: Double) -> Void) {
-
- hexes: for (hex, stableSamples) in stable {
- guard let currentSamples = current[hex] else {
- fatalError("Current sample is missing at \(hex)")
- }
-
- tags: for (tag, stableTuple) in stableSamples {
- guard let currentTuple = currentSamples[tag] else {
- fatalError("Current tuple is missing at \(hex):\(tag)")
- }
-
- channels: for i in [0...2] {
- guard let stableChannel = stableTuple[i].first, let currentChannel = currentTuple[i].first else {
- fatalError("Current channel is missing at \(hex):\(tag):\(i)")
- }
-
- block(hex, tag, stableTuple, currentTuple, stableChannel, currentChannel)
- }
-
- }
- }
-
- }
-}
diff --git a/Tests/HSLuvSwiftTests/iOSExtensions/UIKit.swift b/Tests/HSLuvSwiftTests/iOSExtensions/UIKit.swift
deleted file mode 100644
index 4774e7b..0000000
--- a/Tests/HSLuvSwiftTests/iOSExtensions/UIKit.swift
+++ /dev/null
@@ -1,55 +0,0 @@
-//
-// The MIT License (MIT)
-//
-// Copyright (c) 2015 Clay Smith
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-
-import Foundation
-import XCTest
-import HSLuvSwift
-
-#if canImport(UIKit)
-
-class UIKitTests: XCTestCase {
- let rgbRangeTolerance = 0.00000000001
-
- func testUIColorRGBRangeTolerance() {
- for h in stride(from: 0, through: 360, by: 5) {
- for s in stride(from: 0, through: 100, by: 5) {
- for l in stride(from: 0, through: 100, by: 5) {
- let color = UIColor(hue: Double(h), saturation: Double(s), lightness: Double(l), alpha: 1.0)
-
- XCTAssertNotNil(color)
-
- let tRgb = color.getRGB()
- let rgb = [tRgb.red, tRgb.green, tRgb.blue].map { Double($0) }
-
- for channel in rgb {
- XCTAssertGreaterThan(channel, -rgbRangeTolerance, "HSLuv: \([h, s, l]) -> RGB: \(rgb)")
- XCTAssertLessThanOrEqual(channel, 1 + rgbRangeTolerance, "HSLuv: \([h, s, l]) -> RGB: \(rgb)")
- }
- }
- }
- }
- }
-}
-
-#endif
diff --git a/Tests/HSLuvTests.swift b/Tests/HSLuvTests.swift
new file mode 100644
index 0000000..bd32e8e
--- /dev/null
+++ b/Tests/HSLuvTests.swift
@@ -0,0 +1,122 @@
+// https://www.swift.org/documentation/server/guides/testing.html
+import XCTest
+import Foundation
+@testable import HSLuv
+
+class HSLuvTests: XCTestCase {
+
+ struct Snapshot: Codable {
+ let rgb: [Double]
+ let xyz: [Double]
+ let luv: [Double]
+ let lch: [Double]
+ let hsluv: [Double]
+ let hpluv: [Double]
+ }
+
+ func assertClose(expected: Hsluv, actual: Hsluv, tolerance: Double = 1e-10) {
+ XCTAssertEqual(expected.hex, actual.hex)
+ XCTAssertLessThanOrEqual(abs(expected.rgb_r - actual.rgb_r), tolerance)
+ XCTAssertLessThanOrEqual(abs(expected.rgb_g - actual.rgb_g), tolerance)
+ XCTAssertLessThanOrEqual(abs(expected.rgb_b - actual.rgb_b), tolerance)
+ XCTAssertLessThanOrEqual(abs(expected.xyz_x - actual.xyz_x), tolerance)
+ XCTAssertLessThanOrEqual(abs(expected.xyz_y - actual.xyz_y), tolerance)
+ XCTAssertLessThanOrEqual(abs(expected.xyz_z - actual.xyz_z), tolerance)
+ XCTAssertLessThanOrEqual(abs(expected.luv_l - actual.luv_l), tolerance)
+ XCTAssertLessThanOrEqual(abs(expected.luv_u - actual.luv_u), tolerance)
+ XCTAssertLessThanOrEqual(abs(expected.luv_v - actual.luv_v), tolerance)
+ XCTAssertLessThanOrEqual(abs(expected.lch_l - actual.lch_l), tolerance)
+ XCTAssertLessThanOrEqual(abs(expected.lch_c - actual.lch_c), tolerance)
+ XCTAssertLessThanOrEqual(abs(expected.lch_h - actual.lch_h), tolerance)
+ XCTAssertLessThanOrEqual(abs(expected.hsluv_h - actual.hsluv_h), tolerance)
+ XCTAssertLessThanOrEqual(abs(expected.hsluv_s - actual.hsluv_s), tolerance)
+ XCTAssertLessThanOrEqual(abs(expected.hsluv_l - actual.hsluv_l), tolerance)
+ XCTAssertLessThanOrEqual(abs(expected.hpluv_h - actual.hpluv_h), tolerance)
+ XCTAssertLessThanOrEqual(abs(expected.hpluv_p - actual.hpluv_p), tolerance)
+ XCTAssertLessThanOrEqual(abs(expected.hpluv_l - actual.hpluv_l), tolerance)
+ }
+
+ func testStandaloneFunctions() {
+ // Test that the functions match the class-based implementation
+
+ // hsluvToRgb
+ let converter = Hsluv()
+ converter.hsluv_h = 120
+ converter.hsluv_s = 50
+ converter.hsluv_l = 70
+ converter.hsluvToRgb()
+
+ let standaloneResult = hsluvToRgb(120, 50, 70)
+ XCTAssertEqual(standaloneResult.red, clamp(converter.rgb_r), accuracy: 0.0001)
+ XCTAssertEqual(standaloneResult.green, clamp(converter.rgb_g), accuracy: 0.0001)
+ XCTAssertEqual(standaloneResult.blue, clamp(converter.rgb_b), accuracy: 0.0001)
+
+ // hpluvToRgb
+ converter.hpluv_h = 120
+ converter.hpluv_p = 50
+ converter.hpluv_l = 70
+ converter.hpluvToRgb()
+
+ let standaloneResult2 = hpluvToRgb(120, 50, 70)
+ XCTAssertEqual(standaloneResult2.red, clamp(converter.rgb_r), accuracy: 0.0001)
+ XCTAssertEqual(standaloneResult2.green, clamp(converter.rgb_g), accuracy: 0.0001)
+ XCTAssertEqual(standaloneResult2.blue, clamp(converter.rgb_b), accuracy: 0.0001)
+ }
+
+ private func clamp(_ value: Double) -> Double {
+ return max(0.0, min(1.0, value))
+ }
+
+ func testSnapshot() {
+ let url = URL(fileURLWithPath: "Tests/Resources/snapshot-rev4.json")
+ guard let data = try? Data(contentsOf: url),
+ let snapshot = try? JSONDecoder().decode([String: Snapshot].self, from: data) else {
+ XCTFail("Could not load snapshot-rev4.json")
+ return
+ }
+
+ let conv = Hsluv()
+
+ for (hex, s) in snapshot {
+ let sample = Hsluv()
+ sample.hex = hex
+ sample.rgb_r = s.rgb[0]
+ sample.rgb_g = s.rgb[1]
+ sample.rgb_b = s.rgb[2]
+ sample.xyz_x = s.xyz[0]
+ sample.xyz_y = s.xyz[1]
+ sample.xyz_z = s.xyz[2]
+ sample.luv_l = s.luv[0]
+ sample.luv_u = s.luv[1]
+ sample.luv_v = s.luv[2]
+ sample.lch_l = s.lch[0]
+ sample.lch_c = s.lch[1]
+ sample.lch_h = s.lch[2]
+ sample.hsluv_h = s.hsluv[0]
+ sample.hsluv_s = s.hsluv[1]
+ sample.hsluv_l = s.hsluv[2]
+ sample.hpluv_h = s.hpluv[0]
+ sample.hpluv_p = s.hpluv[1]
+ sample.hpluv_l = s.hpluv[2]
+
+ // Test hex to HSLuv conversion
+ conv.hex = hex
+ conv.hexToHsluv()
+ assertClose(expected: sample, actual: conv)
+
+ // Test HSLuv to hex conversion
+ conv.hsluv_h = sample.hsluv_h
+ conv.hsluv_s = sample.hsluv_s
+ conv.hsluv_l = sample.hsluv_l
+ conv.hsluvToHex()
+ assertClose(expected: sample, actual: conv)
+
+ // Test HPLuv to hex conversion
+ conv.hpluv_h = sample.hpluv_h
+ conv.hpluv_p = sample.hpluv_p
+ conv.hpluv_l = sample.hpluv_l
+ conv.hpluvToHex()
+ assertClose(expected: sample, actual: conv)
+ }
+ }
+}
\ No newline at end of file
diff --git a/Tests/HSLuvSwiftTests/Resources/snapshot-rev4.json b/Tests/Resources/snapshot-rev4.json
similarity index 100%
rename from Tests/HSLuvSwiftTests/Resources/snapshot-rev4.json
rename to Tests/Resources/snapshot-rev4.json