Skip to content

Commit 084690d

Browse files
committed
Merge branch 'macadam-limits' into 'main'
Add MacAdam limits check and mapping See merge request Wacton/Unicolour!96
2 parents a2d4cec + f69d35a commit 084690d

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+1285
-484
lines changed

Example.Diagrams/Program.cs

Lines changed: 62 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using ScottPlot;
22
using ScottPlot.Plottables;
3+
using Wacton.Unicolour.Datasets;
34
using Wacton.Unicolour.Example.Diagrams;
45

56
const string outputDirectory = "../../../../Unicolour.Readme/docs/";
@@ -15,12 +16,18 @@
1516
var rgbGamut = Utils.GetRgbGamut();
1617
var blackbodyLocus = Utils.GetBlackbodyLocus();
1718
var isotherms = Utils.GetIsotherms();
19+
var macAdamLimits = new[]
20+
{
21+
MacAdam.Limits10, MacAdam.Limits20, MacAdam.Limits30, MacAdam.Limits40, MacAdam.Limits50,
22+
MacAdam.Limits60, MacAdam.Limits70, MacAdam.Limits80, MacAdam.Limits90, MacAdam.Limits95
23+
};
1824

1925
SpectralLocus();
2026
XyChromaticityWithRgb();
2127
XyChromaticityWithBlackbody();
22-
UvChromaticity();
28+
XyMacAdamLimits();
2329
UvChromaticityWithBlackbody();
30+
UvMacAdamLimits();
2431
return;
2532

2633
void SpectralLocus()
@@ -129,33 +136,40 @@ void XyChromaticityWithBlackbody()
129136
plot.SavePng(Path.Combine(outputDirectory, "diagram-xy-chromaticity-blackbody.png"), width, height);
130137
}
131138

132-
void UvChromaticity()
139+
void XyMacAdamLimits()
133140
{
134-
var rangeU = (0.0, 0.7);
135-
var rangeV = (0.0, 0.4);
136-
var plot = Utils.GetEmptyPlot(rangeU, rangeV, majorTickInterval: 0.05);
137-
138-
var fillMarkers = Utils.GetUvFillMarkers(rangeU, rangeV, increment: 0.00025);
139-
foreach (var marker in fillMarkers)
140-
{
141-
plot.Add.Plottable(marker);
142-
}
141+
var rangeX = (0.0, 0.8);
142+
var rangeY = (0.0, 0.9);
143+
var plot = Utils.GetEmptyPlot(rangeX, rangeY, majorTickInterval: 0.1);
143144

144-
var spectralCoordinates = spectralLocus.Select(colour => new Coordinates(colour.Chromaticity.U, colour.Chromaticity.V)).ToList();
145+
var spectralCoordinates = spectralLocus.Select(colour => new Coordinates(colour.Chromaticity.X, colour.Chromaticity.Y)).ToList();
145146
spectralCoordinates.Add(spectralCoordinates.First());
146147
var spectralLocusScatter = plot.Add.Scatter(spectralCoordinates);
147148
spectralLocusScatter.Color = Colors.Black;
148149
spectralLocusScatter.LineWidth = 2.5f;
149150
spectralLocusScatter.MarkerStyle = MarkerStyle.None;
150151

151-
var width = Utils.GetWidth(rangeU, rangeV, height);
152-
plot.SavePng(Path.Combine(outputDirectory, "diagram-uv-chromaticity.png"), width, height);
152+
foreach (var limits in macAdamLimits)
153+
{
154+
var coordinates = limits.Select(colour => new Coordinates(colour.Chromaticity.X, colour.Chromaticity.Y)).ToList();
155+
coordinates.Add(coordinates.First());
156+
var scatter = plot.Add.Scatter(coordinates);
157+
158+
var luminance = limits.First().RelativeLuminance; // all colours in the group will have the same luminance
159+
var mapColour = Colourmaps.Flare.Map(1 - luminance).Rgb.Byte255;
160+
var color = new Color((byte)mapColour.R, (byte)mapColour.G, (byte)mapColour.B);
161+
scatter.LineStyle = new LineStyle { Color = color, Width = 2.5f };
162+
scatter.MarkerStyle = MarkerStyle.None;
163+
}
164+
165+
var width = Utils.GetWidth(rangeX, rangeY, height);
166+
plot.SavePng(Path.Combine(outputDirectory, "diagram-xy-macadam-limits.png"), width, height);
153167
}
154168

155169
void UvChromaticityWithBlackbody()
156170
{
157-
var rangeU = (0.1, 0.45);
158-
var rangeV = (0.25, 0.4);
171+
var rangeU = (0.0, 0.7);
172+
var rangeV = (0.0, 0.4);
159173
var plot = Utils.GetEmptyPlot(rangeU, rangeV, majorTickInterval: 0.05);
160174

161175
var fillMarkers = Utils.GetUvFillMarkers(rangeU, rangeV, increment: 0.00025);
@@ -170,13 +184,13 @@ void UvChromaticityWithBlackbody()
170184
spectralLocusScatter.Color = Colors.Black;
171185
spectralLocusScatter.LineWidth = 2.5f;
172186
spectralLocusScatter.MarkerStyle = MarkerStyle.None;
173-
187+
174188
var blackbodyCoordinates = blackbodyLocus.Select(colour => new Coordinates(colour.Chromaticity.U, colour.Chromaticity.V)).ToList();
175189
var blackbodyScatter = plot.Add.Scatter(blackbodyCoordinates);
176190
blackbodyScatter.Color = Colors.Black;
177191
blackbodyScatter.LineWidth = 2.5f;
178192
blackbodyScatter.MarkerStyle = MarkerStyle.None;
179-
193+
180194
foreach (var (startColour, endColour) in isotherms)
181195
{
182196
var startCoordinates = new Coordinates(startColour.Chromaticity.U, startColour.Chromaticity.V);
@@ -188,4 +202,34 @@ void UvChromaticityWithBlackbody()
188202

189203
var width = Utils.GetWidth(rangeU, rangeV, height);
190204
plot.SavePng(Path.Combine(outputDirectory, "diagram-uv-chromaticity-blackbody.png"), width, height);
205+
}
206+
207+
void UvMacAdamLimits()
208+
{
209+
var rangeU = (0.0, 0.7);
210+
var rangeV = (0.0, 0.4);
211+
var plot = Utils.GetEmptyPlot(rangeU, rangeV, majorTickInterval: 0.05);
212+
213+
var spectralCoordinates = spectralLocus.Select(colour => new Coordinates(colour.Chromaticity.U, colour.Chromaticity.V)).ToList();
214+
spectralCoordinates.Add(spectralCoordinates.First());
215+
var spectralLocusScatter = plot.Add.Scatter(spectralCoordinates);
216+
spectralLocusScatter.Color = Colors.Black;
217+
spectralLocusScatter.LineWidth = 2.5f;
218+
spectralLocusScatter.MarkerStyle = MarkerStyle.None;
219+
220+
foreach (var limits in macAdamLimits)
221+
{
222+
var coordinates = limits.Select(colour => new Coordinates(colour.Chromaticity.U, colour.Chromaticity.V)).ToList();
223+
coordinates.Add(coordinates.First());
224+
var scatter = plot.Add.Scatter(coordinates);
225+
226+
var luminance = limits.First().RelativeLuminance; // all colours in the group will have the same luminance
227+
var mapColour = Colourmaps.Flare.Map(1 - luminance).Rgb.Byte255;
228+
var color = new Color((byte)mapColour.R, (byte)mapColour.G, (byte)mapColour.B);
229+
scatter.LineStyle = new LineStyle { Color = color, Width = 2.5f };
230+
scatter.MarkerStyle = MarkerStyle.None;
231+
}
232+
233+
var width = Utils.GetWidth(rangeU, rangeV, height);
234+
plot.SavePng(Path.Combine(outputDirectory, "diagram-uv-macadam-limits.png"), width, height);
191235
}

Example.Gradients/Utils.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
using SixLabors.ImageSharp.Drawing.Processing;
44
using SixLabors.ImageSharp.PixelFormats;
55
using SixLabors.ImageSharp.Processing;
6-
using Wacton.Unicolour.Datasets;
76

87
namespace Wacton.Unicolour.Example.Gradients;
98

Unicolour.Datasets/MacAdam.cs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
namespace Wacton.Unicolour.Datasets;
2+
3+
// https://doi.org/10.2307/1420820 Table II(3.7)
4+
public static class MacAdam
5+
{
6+
private static readonly Configuration Config = new(RgbConfiguration.StandardRgb, XyzConfiguration.D65);
7+
8+
public static readonly Unicolour[] Limits10 = MacAdamLimits.Get(0.10).Select(chromaticity => new Unicolour(Config, chromaticity, 0.10)).ToArray();
9+
public static readonly Unicolour[] Limits20 = MacAdamLimits.Get(0.20).Select(chromaticity => new Unicolour(Config, chromaticity, 0.20)).ToArray();
10+
public static readonly Unicolour[] Limits30 = MacAdamLimits.Get(0.30).Select(chromaticity => new Unicolour(Config, chromaticity, 0.30)).ToArray();
11+
public static readonly Unicolour[] Limits40 = MacAdamLimits.Get(0.40).Select(chromaticity => new Unicolour(Config, chromaticity, 0.40)).ToArray();
12+
public static readonly Unicolour[] Limits50 = MacAdamLimits.Get(0.50).Select(chromaticity => new Unicolour(Config, chromaticity, 0.50)).ToArray();
13+
public static readonly Unicolour[] Limits60 = MacAdamLimits.Get(0.60).Select(chromaticity => new Unicolour(Config, chromaticity, 0.60)).ToArray();
14+
public static readonly Unicolour[] Limits70 = MacAdamLimits.Get(0.70).Select(chromaticity => new Unicolour(Config, chromaticity, 0.70)).ToArray();
15+
public static readonly Unicolour[] Limits80 = MacAdamLimits.Get(0.80).Select(chromaticity => new Unicolour(Config, chromaticity, 0.80)).ToArray();
16+
public static readonly Unicolour[] Limits90 = MacAdamLimits.Get(0.90).Select(chromaticity => new Unicolour(Config, chromaticity, 0.90)).ToArray();
17+
public static readonly Unicolour[] Limits95 = MacAdamLimits.Get(0.95).Select(chromaticity => new Unicolour(Config, chromaticity, 0.95)).ToArray();
18+
19+
public static IEnumerable<Unicolour> All => new List<Unicolour>()
20+
.Concat(Limits10)
21+
.Concat(Limits20)
22+
.Concat(Limits30)
23+
.Concat(Limits40)
24+
.Concat(Limits50)
25+
.Concat(Limits60)
26+
.Concat(Limits70)
27+
.Concat(Limits80)
28+
.Concat(Limits90)
29+
.Concat(Limits95);
30+
}

Unicolour.Readme/Program.cs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -198,11 +198,15 @@ void FeatureCompare()
198198
void FeatureGamutMap()
199199
{
200200
var veryRed = new Unicolour(ColourSpace.Rgb, 1.25, -0.39, -0.14);
201-
var isInRgb = veryRed.IsInRgbGamut;
202-
var normalRed = veryRed.MapToRgbGamut();
203-
204-
var isInPointer = veryRed.IsInPointerGamut;
205-
var surfaceRed = veryRed.MapToPointerGamut();
201+
202+
var isDisplayable = veryRed.IsInRgbGamut;
203+
var displayRed = veryRed.MapToRgbGamut();
204+
205+
var isEmpiricalSurface = veryRed.IsInPointerGamut;
206+
var empiricalRed = veryRed.MapToPointerGamut();
207+
208+
var isTheoreticalSurface = veryRed.IsInMacAdamLimits;
209+
var theoreticalRed = veryRed.MapToMacAdamLimits();
206210
}
207211

208212
void FeatureCvd()

Unicolour.Readme/README.md

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
[![GitLab](https://badgen.net/static/gitlab/source/ff1493?icon=gitlab)](https://gitlab.com/Wacton/Unicolour)
44
[![NuGet](https://badgen.net/nuget/v/Wacton.Unicolour?icon)](https://www.nuget.org/packages/Wacton.Unicolour/)
55
[![pipeline status](https://gitlab.com/Wacton/Unicolour/badges/main/pipeline.svg)](https://gitlab.com/Wacton/Unicolour/-/commits/main)
6-
[![tests passed](https://badgen.net/static/tests/236,405/green/)](https://gitlab.com/Wacton/Unicolour/-/pipelines)
6+
[![tests passed](https://badgen.net/static/tests/237,247/green/)](https://gitlab.com/Wacton/Unicolour/-/pipelines)
77
[![coverage report](https://gitlab.com/Wacton/Unicolour/badges/main/coverage.svg)](https://gitlab.com/Wacton/Unicolour/-/pipelines)
88

99
Unicolour is the most comprehensive .NET library for working with colour:
@@ -384,14 +384,19 @@ var difference = red.Difference(blue, DeltaE.Cie76);
384384

385385
### Map colour into gamut
386386
Colours that cannot be displayed with the [configured RGB model](#rgbconfiguration) can be mapped to the closest in-gamut RGB colour.
387-
Mapping to Pointer's gamut will return the closest real surface colour of the same lightness and hue.
387+
Mapping to Pointer's gamut will return the closest empirically real surface colour of the same lightness and hue.
388+
Mapping to MacAdam limits will return the closest theoretically real surface colour of the same wavelength and luminance.
388389
```c#
389390
var veryRed = new Unicolour(ColourSpace.Rgb, 1.25, -0.39, -0.14);
390-
var isInRgb = veryRed.IsInRgbGamut;
391-
var normalRed = veryRed.MapToRgbGamut();
392391

393-
var isInPointer = veryRed.IsInPointerGamut;
394-
var surfaceRed = veryRed.MapToPointerGamut();
392+
var isDisplayable = veryRed.IsInRgbGamut;
393+
var displayRed = veryRed.MapToRgbGamut();
394+
395+
var isEmpiricalSurface = veryRed.IsInPointerGamut;
396+
var empiricalRed = veryRed.MapToPointerGamut();
397+
398+
var isTheoreticalSurface = veryRed.IsInMacAdamLimits;
399+
var theoreticalRed = veryRed.MapToMacAdamLimits();
395400
```
396401

397402
| RGB&nbsp;gamut&nbsp;mapping&nbsp;method | Enum |
@@ -844,18 +849,22 @@ can be seen in the [Example.Diagrams](../Example.Diagrams/Program.cs) project.
844849
|--------------------------------------------------------------------------------------------------------------------------------------|
845850
| _CIE xy chromaticity diagram with Planckian or blackbody locus_ |
846851

852+
| ![CIE xy chromaticity diagram with MacAdam limits, created with Unicolour](docs/diagram-xy-macadam-limits.png) |
853+
|----------------------------------------------------------------------------------------------------------------|
854+
| _CIE xy chromaticity diagram with MacAdam limits_ |
855+
847856
| ![CIE xy chromaticity diagram with spectral locus plotted at 1 nm intervals, created with Unicolour](docs/diagram-spectral-locus.png) |
848857
|---------------------------------------------------------------------------------------------------------------------------------------|
849858
| _CIE xy chromaticity diagram with spectral locus plotted at 1 nm intervals_ |
850859

851-
| ![CIE 1960 colour space, created with Unicolour](docs/diagram-uv-chromaticity.png) |
852-
|------------------------------------------------------------------------------------|
853-
| _CIE 1960 colour space_ |
854-
855860
| ![CIE 1960 colour space with Planckian or blackbody locus, created with Unicolour](docs/diagram-uv-chromaticity-blackbody.png) |
856861
|--------------------------------------------------------------------------------------------------------------------------------|
857862
| _CIE 1960 colour space with Planckian or blackbody locus_ |
858863

864+
| ![CIE 1960 colour space with MacAdam limits, created with Unicolour](docs/diagram-uv-macadam-limits.png) |
865+
|----------------------------------------------------------------------------------------------------------|
866+
| _CIE 1960 colour space with MacAdam limits_ |
867+
859868
### Console
860869
Example code to create a colourful console application using ⌨️ [Spectre.Console](https://github.com/spectreconsole/spectre.console)
861870
can be seen in the [Example.Console](../Example.Console/Program.cs) project.
@@ -907,6 +916,7 @@ Perceptually uniform colourmaps / palettes:
907916
- [Cubehelix](https://people.phy.cam.ac.uk/dag9/CUBEHELIX/) (sequential)
908917

909918
Colour data used in academic literature:
919+
- [MacAdam](https://doi.org/10.2307/1420820) limits of surface colours
910920
- [Hung-Berns](https://doi.org/10.1002/col.5080200506) constant hue loci data
911921
- [Ebner-Fairchild](https://doi.org/10.1117/12.298269) constant perceived-hue data
912922

Unicolour.Readme/Unicolour.Readme.csproj

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,15 +67,18 @@
6767
<None Update="docs\diagram-xy-chromaticity-blackbody.png">
6868
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
6969
</None>
70-
<None Update="docs\diagram-spectral-locus.png">
70+
<None Update="docs\diagram-xy-macadam-limits.png">
7171
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
7272
</None>
73-
<None Update="docs\diagram-uv-chromaticity.png">
73+
<None Update="docs\diagram-spectral-locus.png">
7474
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
7575
</None>
7676
<None Update="docs\diagram-uv-chromaticity-blackbody.png">
7777
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
7878
</None>
79+
<None Update="docs\diagram-uv-macadam-limits.png">
80+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
81+
</None>
7982
<None Update="docs\console-info.png">
8083
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
8184
</None>
-130 KB
Loading
-134 KB
Binary file not shown.
98.8 KB
Loading
99.1 KB
Loading

0 commit comments

Comments
 (0)