feat: initial commit

This commit is contained in:
Jonas Juselius
2022-06-02 15:22:28 +02:00
parent 44bc71572b
commit 48c88b476f
100 changed files with 21611 additions and 8570 deletions

View File

@@ -1,5 +1,9 @@
# Matplotlib.ColorMaps
This code is based on the [SciColorMaps](https://github.com/ar1st0crat/SciColorMaps) project by Tim Sharii,
which in turn is based on the [matplotlib](https://matplotlib.org/3.5.0/tutorials/colors/colormaps.html)
Python colormap implementation(s).
## Run
`dotnet run`

View File

@@ -1,63 +0,0 @@
###############################################################################
# Set default behavior to automatically normalize line endings.
###############################################################################
* text=auto
###############################################################################
# Set default behavior for command prompt diff.
#
# This is need for earlier builds of msysgit that does not have it on by
# default for csharp files.
# Note: This is only used by command line
###############################################################################
#*.cs diff=csharp
###############################################################################
# Set the merge driver for project and solution files
#
# Merging from the command prompt will add diff markers to the files if there
# are conflicts (Merging from VS is not affected by the settings below, in VS
# the diff markers are never inserted). Diff markers may cause the following
# file extensions to fail to load in VS. An alternative would be to treat
# these files as binary and thus will always conflict and require user
# intervention with every merge. To do so, just uncomment the entries below
###############################################################################
#*.sln merge=binary
#*.csproj merge=binary
#*.vbproj merge=binary
#*.vcxproj merge=binary
#*.vcproj merge=binary
#*.dbproj merge=binary
#*.fsproj merge=binary
#*.lsproj merge=binary
#*.wixproj merge=binary
#*.modelproj merge=binary
#*.sqlproj merge=binary
#*.wwaproj merge=binary
###############################################################################
# behavior for image files
#
# image files are treated as binary by default.
###############################################################################
#*.jpg binary
#*.png binary
#*.gif binary
###############################################################################
# diff behavior for common document formats
#
# Convert binary document formats to text before diffing them. This feature
# is only available from the command line. Turn it on by uncommenting the
# entries below.
###############################################################################
#*.doc diff=astextplain
#*.DOC diff=astextplain
#*.docx diff=astextplain
#*.DOCX diff=astextplain
#*.dot diff=astextplain
#*.DOT diff=astextplain
#*.pdf diff=astextplain
#*.PDF diff=astextplain
#*.rtf diff=astextplain
#*.RTF diff=astextplain

View File

@@ -1,252 +0,0 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
# Visual Studio 2015 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUNIT
*.VisualState.xml
TestResult.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# DNX
project.lock.json
artifacts/
*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding add-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# TODO: Comment the next line if you want to checkin your web deploy settings
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/packages/*
# except build/, which is used as an MSBuild target.
!**/packages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/packages/repositories.config
# NuGet v3's project.json files produces more ignoreable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.pfx
*.publishsettings
node_modules/
orleans.codegen.cs
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
# SQL Server files
*.mdf
*.ldf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# JetBrains Rider
.idea/
*.sln.iml

View File

@@ -1,21 +0,0 @@
MIT License
Copyright (c) 2017 Tim
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.

View File

@@ -1,258 +0,0 @@
# SciColorMaps
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
![NuGet](https://img.shields.io/nuget/dt/ar1st0crat.SciColorMaps.Portable)
Simple and convenient library providing custom .NET color maps (user-defined or imported from matplotlib) for scientific visualization.
Library comes in two versions (also available as NuGet packages):
- **SciColorMaps.Portable.dll** does not rely on any framework-dependent color structure (like ```System.Drawing.Color``` or ```System.Windows.Media.Color```). It operates with pure byte arrays, so it can be used in any .NET project.
- **SciColorMaps.dll** provides some additional functions that handle GDI+ colors, so it can be used in WinForms and WPF projects.
*NuGet packages contain binaries with all 75 matplotlib colormaps included. In most cases only a small subset of them is what is really needed. Luckily, SciColorMaps can be customized and recompiled quite easily. This repo includes special helper script for making your own SciColorMaps dll. Please [read instructions here](#compiling-scicolormaps) how to use it.*
## Base ColorMap class
Usage example:
```C#
// create 'viridis' colormap with 256 colors, mapping values from range [0, 1]
var cmap1 = new ColorMap();
// create 'rainbow' colormap with 256 colors, mapping values from range [0, 1]
var cmap2 = new ColorMap("rainbow");
// create 'gnuplot' colormap with 256 colors, mapping values from range [-7.5, 7.5]
var cmap3 = new ColorMap("gnuplot", -7.5, 7.5);
// create 'coolwarm' colormap with 64 colors, mapping values from range [10, 100]
var cmap = new ColorMap("coolwarm", 10, 100, 64);
// get color corresponding to value 25
var color = cmap[25];
// print names of all available predefined palettes
foreach (var palette in ColorMap.Palettes)
{
Console.WriteLine(palette);
}
// prints full set of 75 matplotlib colormaps:
// accent
// afmhot
// autumn
// ...
// ylorbr
// ylorrd
```
![pic1](https://github.com/ar1st0crat/SciColorMaps/blob/master/Screenshots/WinForms.png)
![pic2](https://github.com/ar1st0crat/SciColorMaps/blob/master/Screenshots/WinForms2.png)
## User-defined colormaps
Users can create their own color palettes, like this:
```C#
// === works for both versions of SciColorMaps ===
var rgbs = new [] {
new byte[] {0, 0, 0},
new byte[] {192, 0, 0},
new byte[] {255, 224, 255}
};
var positions = new float[] { 0, 0.4f, 1 };
// 1) static factory method with default colormap parameters:
var cmap1 = ColorMap.CreateFromColors(rgbs, positions);
// 2) static factory method, full set of parameters:
var cmap2 = ColorMap.CreateFromColors(rgbs, positions, 10, 100, 32);
// 3) ColorMap constructor:
var cmap3 = new ColorMap("my_own_colormap", -2, 2, 128, rgbs, positions);
// === won't compile for SciColorMaps.Portable ===
var colors = new Color[] { Color.Black, Color.Blue, Color.White };
// 4) static factory method with default colormap parameters:
var cmap4 = ColorMap.CreateFromColors(colors, positions);
// 5) static factory method, full set of parameters:
var cmap5 = ColorMap.CreateFromColors(colors, positions, 10, 100, 32);
// 6) ColorMap constructor:
var cmap6 = new ColorMap("fancy_colormap", 10, 100, 32, colors, positions);
```
Options 3 and 6 allow user setting the name of the custom colormap. Otherwise the name is set by default: "user".
Note also, the first color position should be 0.0f and last position should be 1.0f (otherwise an ArgumentException will be thrown).
*Byte arrays can be easily converted to any framework-specific color structures/classes.*
For example, in WPF the following extension method can be used:
```C#
using SciColorMaps.Portable;
public static class ColorUtils
{
public static Color ToMediaColor(this byte[] rgb)
{
return Color.FromRgb(rgb[0], rgb[1], rgb[2]);
}
}
...
var cmap = new ColorMap("ocean");
var color = cmap[0.3].ToMediaColor();
```
![User-defined](https://github.com/ar1st0crat/SciColorMaps/blob/master/Screenshots/WinFormsCustom.png)
## Grayscale colormaps
SciColorMaps provides the ```GrayColorMap``` decorator class. This class replaces base palette with its grayscale analog during construction.
Usage example:
```C#
// upper sreenshot
var cmap1 = new GrayColorMap(new ColorMap("gnuplot2", min, max));
// lower screenshot
var cmap2 = new GrayColorMap(new ColorMap("gnuplot2", min, max), GrayScaleOptions.Lightness);
// the three simple algorithms for conversion are implemented
// (as in GIMP: https://docs.gimp.org/2.6/en/gimp-tool-desaturate.html)
// public enum GrayScaleOptions
// {
// Luminosity, // gray = 0.21*R + 0.72*G + 0.07*B
// Lightness, // gray = (max(R, G, B) + min(R, G, B)) / 2
// Average // gray = (R + G + B) / 3
// }
// GrayScaleOptions.Luminosity is used by default
```
![Grayscale](https://github.com/ar1st0crat/SciColorMaps/blob/master/Screenshots/WinFormsGray.png)
## Mirrored colormaps
SciColorMaps also provides the ```MirrorColorMap``` decorator class. This class replaces base palette with its mirrored analog during construction.
Usage example:
```C#
// mirrored colors
var cmap = new MirrorColorMap(new ColorMap("ocean"));
```
## WinForms demo app
Left button click on surface 2d area -> change colormap to its mirrored version.
Right button click on surface 2d area -> change colormap to its grayscale version.
Left button click on colormap strip -> open dialog for constructing your own palette.
## WPF demo app
Shows how to use SciColorMaps.Portable.
![WPF](https://github.com/ar1st0crat/SciColorMaps/blob/master/Screenshots/Wpf.png)
## UWP demo app
Shows how to use SciColorMaps.Portable in UWP projects.
The app allows loading sound from wav/mp3/mp4 files as well as recording sound from any input device (most likely, the microphone). When the audio signal is loaded, the app evaluates and visualizes its spectrogram in 2D and 3D. The visualization is based on the colormap selected in the left panel of a main page. Check it out: 3D-spectrogram with 'blues' colormap looks just like mountains in the fog ))
![UWP](https://github.com/ar1st0crat/SciColorMaps/blob/master/Screenshots/Uwp.png)
## Compiling SciColorMaps
All customizable parameters are contained in ```Palette.cs``` file. So there's a helper script that can generate this file according to particular needs. Here's how to use it:
1) Download or clone this repo
2) Go to ```matplotlib``` folder and edit ```colormaps.txt``` text file, e.g.:
```
ocean
hot
coolwarm
```
Basically you include here only colormaps you really need. You can view all available colormap names in ```colormaps``` subfolder. If you specify non-existing colormap it'll be simply ignored.
Note also that 'viridis' will be included to the list anyway since it's the default modern colormap.
3) run ```generate_cs.py``` script:
```
python generate_cs.py [-r 16] [-p] [-a]
```
```-r``` - colormap resolution (number of base colors; should be 8, 16, 32, 64, 128 or 256)
```-p``` - if specified then generate cs file for portable SciColorMaps version
```-a``` - if specified then generate cs file with all palettes found in ```colormaps``` folder (ignoring ```colormaps.txt``` input file)
Examples:
```
// generate cs file with all palettes found in colormaps folder
// (ignore colormaps.txt) and resolution = 8 base colors
python generate_cs.py -r 8 -a
// generate cs file for portable version
// (with default resolution = 16 base colors)
python generate_cs.py -p
```
4) go to ```cs``` subfolder and copy generated ```Palette.cs``` file to ```SciColorMaps``` or ```SciColorMaps.Portable``` project folder.
5) open ```SciColorMaps.sln``` in Visual Studio and compile new dll.
**RECTANGULAR conditional compilation symbol**
Accessing elements in jagged arrays is significantly faster (up to **40%**) compared to rectangular arrays, hence the jagged arrays are used and compiled by default. If an efficient memory management is of more importance then compile SciColorMaps with the 'RECTANGULAR' conditional compilation symbol.
| compilation | dll size |
----------------|-----------
| default | ~95kb |
| RECTANGULAR | ~29kb |
Also, if you are worried about the memory usage, please note that palette arrays are instantiated *lazily* in the calling code, e.g.:
```C#
var palette = Palette.GnuPlot.Value;
```

View File

@@ -1,311 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace SciColorMaps.Portable
{
/// <summary>
/// Color map
/// 1) uses particular color palette (as in matplotlib or user-defined),
/// 2) automatically calculates color by value and range,
/// 3) can work with any number of colors in range [2, 256]
///
/// Usage example:
///
/// var cmap = new ColorMap("coolwarm", -0.5, 0.5));
/// ...
/// var color = cmap[0.3];
///
/// var viridis = new ColorMap();
///
/// var cm = new ColorMap("ocean", 10, 100, 32 /*colors*/)
///
///
/// All standard matplotlib palettes are available including:
/// bone, cool, coolwarm, gist_earth, gnuplot, gnuplot2, hot, inferno,
/// jet, ocean, rainbow, seismic, spectral, terrain, viridis (default), etc.
///
/// Users can create their own palettes, like this:
///
/// var colors = new [] {
/// new byte[] {0, 0, 0},
/// new byte[] {192, 0, 0},
/// new byte[] {255, 224, 255}
/// };
/// var positions = new [] { 0, 0.4f, 1 };
///
/// // 1) static factory method with default colormap parameters:
/// var cmap1 = ColorMap.CreateFromColors(colors, positions);
///
/// // 2) static factory method, full set of parameters:
/// var cmap2 = ColorMap.CreateFromColors(colors, positions, 10, 100, 32);
///
/// // 3) ColorMap constructor:
/// var cmap3 = new ColorMap("my_own_colormap", 10, 100, 32, colors, positions);
///
/// // Option 3 allows user setting the name of the custom colormap
/// // Otherwise the name is set by default: "user"
///
/// </summary>
public class ColorMap
{
/// <summary>
/// Color palette is an array of predefined RGB values
/// </summary>
protected byte[][] _palette;
/// <summary>
/// Number of colors in colormap
/// </summary>
private readonly int _colorCount;
/// <summary>
/// Lower bound of the colormap range
/// </summary>
private readonly double _lower;
/// <summary>
/// Upper bound of the colormap range
/// </summary>
private readonly double _upper;
/// <summary>
/// Range of values corresponding to one color in the colormap
/// (is calculated in the constructor based on given parameters)
/// </summary>
private readonly double _colorRange;
/// <summary>
/// Number of palette colors corresponding to one color in the colormap
/// (is calculated in the constructor based on given parameters)
/// </summary>
private readonly double _colorBinSize;
/// <summary>
/// Palette name ("jet", "viridis", "terrain", etc.)
/// </summary>
public string PaletteName { get; private set; }
/// <summary>
/// The number of interpolated colors in palettes (full palette)
/// (currently 256, but it is customizable)
/// </summary>
public const int PaletteColors = 256;
/// <summary>
/// Return collection of available palettes
/// </summary>
public static IEnumerable<string> Palettes => Palette.Names;
private const string DefaultPalette = "viridis";
/// <summary>
/// Default constructor creates Viridis colormap
/// </summary>
public ColorMap() : this(DefaultPalette)
{
}
/// <summary>
/// Copy constructor
/// </summary>
/// <param name="colorMap">Copied colormap</param>
public ColorMap(ColorMap colorMap)
{
PaletteName = colorMap.PaletteName;
_palette = colorMap._palette;
_lower = colorMap._lower;
_upper = colorMap._upper;
_colorCount = colorMap._colorCount;
_colorRange = colorMap._colorRange;
_colorBinSize = colorMap._colorBinSize;
}
/// <summary>
/// Construct new colormap
/// </summary>
/// <param name="name">Palette name</param>
/// <param name="lower">Lower bound of the colormap range (optional)</param>
/// <param name="upper">Upper bound of the colormap range (optional)</param>
/// <param name="colorCount">Number of colors in colormap (optional)</param>
/// <param name="colors">User-defined collection of colors (optional)</param>
/// <param name="positions">User-defined collection of color positions in palette (optional)</param>
/// <exception cref="ArgumentException">Thrown if:
/// 1) Palette name is null
/// 2) Number of colors is less than 2 or greater than number of colors in palette
/// 3) Lower bound is greater than the upper one
/// </exception>
public ColorMap(string name,
double lower = 0.0,
double upper = 1.0,
int colorCount = PaletteColors,
IEnumerable<byte[]> colors = null,
IEnumerable<float> positions = null)
{
if (name == null)
{
throw new ArgumentException("Palette name should not be null!");
}
if (colorCount <= 1 || colorCount > PaletteColors)
{
throw new ArgumentException(string.Format(
"Number of colors should be in range [2, {0}]!", PaletteColors));
}
if (lower >= upper)
{
throw new ArgumentException("Upper bound should be greater than the lower one!");
}
_colorCount = colorCount;
_lower = lower;
_upper = upper;
_colorRange = (_upper - _lower) / _colorCount;
_colorBinSize = PaletteColors / (_colorCount - 1 + 1.0 / PaletteColors);
// create colormap from a predefined palette:
if (colors == null)
{
PaletteName = name.ToLower();
if (!Palette.ByName.ContainsKey(PaletteName))
{
PaletteName = DefaultPalette;
}
positions = Enumerable.Range(0, Palette.Resolution)
.Select(pos => (float)pos / (Palette.Resolution - 1))
.ToArray();
CreatePalette(Palette.ByName[PaletteName].Value, positions);
}
// create colormap from user-defined colors:
else
{
CreatePalette(colors, positions);
}
}
/// <summary>
/// Static factory method for creating user-defined palette
/// </summary>
/// <param name="colors">Collection of colors as byte[3] arrays</param>
/// <returns>ColorMap object</returns>
public static ColorMap CreateFromColors(IEnumerable<byte[]> colors,
IEnumerable<float> positions,
double lower = 0.0,
double upper = 1.0,
int colorCount = PaletteColors)
{
return new ColorMap("user", lower, upper, colorCount, colors, positions);
}
/// <summary>
/// Method performs a simple RGB color interpolation for creating user-defined palette
/// </summary>
/// <param name="colors">Collection of colors</param>
/// <param name="positions">Collection of color positions</param>
/// <exception cref="ArgumentException">Thrown if:
/// 1) Collection of colors or color positions is null
/// 2) Number of colors is not the same as the number of color positions
/// 3) Number of colors is not in the range [2, PaletteColors]
/// 4) First color position is not 0.0f or last position is not 1.0f
/// </exception>
private void CreatePalette(IEnumerable<byte[]> colors, IEnumerable<float> positions)
{
if (colors == null || positions == null)
{
throw new ArgumentException("Collections of colors and positions should not be null!");
}
if (colors.Count() != positions.Count())
{
throw new ArgumentException("Number of colors should be the same as the number of color positions!");
}
if (colors.Count() <= 1 || colors.Count() > PaletteColors)
{
throw new ArgumentException(string.Format(
"Number of colors should be in range [2, {0}]!", PaletteColors));
}
if (positions.First().CompareTo(0) != 0 || positions.Last().CompareTo(1) != 0)
{
throw new ArgumentException("First color position should be 0.0f and last position should be 1.0f!");
}
if (colors.Any(c => c.Length != 3))
{
throw new ArgumentException("Each color should be an array of 3 bytes!");
}
_palette = new byte[PaletteColors][];
var groups = positions.Select(pos => (int)(pos * PaletteColors)).ToList();
var i = 0;
for (var group = 0; group < groups.Count - 1; group++)
{
var color1 = colors.ElementAt(group);
var color2 = colors.ElementAt(group + 1);
var groupSize = groups[group + 1] - groups[group];
for (var j = 0; j < groupSize; j++)
{
_palette[i] = new byte[3];
_palette[i][0] = (byte)(color1[0] + (double)(color2[0] - color1[0]) * j / groupSize);
_palette[i][1] = (byte)(color1[1] + (double)(color2[1] - color1[1]) * j / groupSize);
_palette[i][2] = (byte)(color1[2] + (double)(color2[2] - color1[2]) * j / groupSize);
i++;
}
}
}
/// <summary>
/// Method returns colorCount-sized collection of base colors covering entire palette
/// </summary>
/// <returns>Collection of colors</returns>
public IEnumerable<byte[]> Colors()
{
for (var i = 0; i < _colorCount; i++)
{
var idx = (int)(i * _colorBinSize);
yield return _palette[idx];
}
}
/// <summary>
/// Get RGB values corresponding to a given domain value
/// </summary>
/// <param name="value">Particular domain value</param>
/// <returns>Array of 3 bytes (R, G, B)</returns>
public byte[] this[double value]
{
get
{
if (value <= _lower)
{
return _palette[0];
}
if (value >= _upper)
{
return _palette[PaletteColors - 1];
}
// get position of the closest color with current resolution
var pos = (int)((value - _lower) / _colorRange);
// get the index of this color in palette
var idx = (int)(pos * _colorBinSize);
return _palette[idx];
}
}
}
}

View File

@@ -1,78 +0,0 @@
using System;
using System.Linq;
namespace SciColorMaps.Portable
{
/// <summary>
/// ColorMap decorator that converts all colors of decorated colormap to grayscale
///
/// Usage example:
///
/// var cmap = new GrayColorMap(new ColorMap("viridis", -0.5, 0.5));
/// ...
/// var color = cmap[0.3];
///
/// Important:
/// Method allocates NEW memory for palette array
/// so that it doesn't affect predefined palettes
///
/// </summary>
public class GrayColorMap : ColorMap
{
/// <summary>
/// Grayscale conversion options
/// </summary>
private readonly GrayScaleOptions _options;
/// <summary>
/// Recalculate palette colors each using one of 3 simple formulae
/// </summary>
private void MakePaletteGrayscale()
{
var prevPalette = _palette;
// important: create new array for palette
_palette = new byte[PaletteColors][];
Func<byte[], byte> convertToGray;
switch (_options)
{
case GrayScaleOptions.Lightness:
convertToGray = color => (byte)((color.Max() + color.Min()) / 2);
break;
case GrayScaleOptions.Average:
convertToGray = color => (byte)((color[0] + color[1] + color[2]) / 3);
break;
default:
convertToGray = color => (byte)(color[0] * 0.21 + color[1] * 0.72 + color[2] * 0.07);
break;
}
for (var i = 0; i < PaletteColors; i++)
{
var gray = convertToGray(prevPalette[i]);
_palette[i] = new [] { gray, gray, gray };
}
}
/// <summary>
/// Constructor (also calls copy constructor of the base ColorMap class)
///
/// We gently substitute ordinary palette with grayscale palette
///
/// </summary>
/// <param name="colorMap">Decorated colormap</param>
/// <param name="options">Luminosity conversion by default</param>
public GrayColorMap(ColorMap colorMap,
GrayScaleOptions options = GrayScaleOptions.Luminosity)
: base(colorMap)
{
_options = options;
// magic goes on here:
MakePaletteGrayscale();
}
}
}

View File

@@ -1,13 +0,0 @@
namespace SciColorMaps.Portable
{
/// <summary>
/// Three options for how to convert RGB color to grayscale
/// (as used in GIMP: https://docs.gimp.org/2.6/en/gimp-tool-desaturate.html)
/// </summary>
public enum GrayScaleOptions
{
Luminosity,
Lightness,
Average
}
}

View File

@@ -1,49 +0,0 @@
namespace SciColorMaps.Portable
{
/// <summary>
/// ColorMap decorator that mirrors colors of decorated colormap
///
/// Usage example:
///
/// var cmap = new MirrorColorMap(new ColorMap("ocean"));
/// ...
/// var color = cmap[0.3];
///
/// Important:
/// Method allocates NEW memory for palette array
/// so that it doesn't affect predefined palettes
///
/// </summary>
public class MirrorColorMap : ColorMap
{
/// <summary>
/// Recalculate palette colors
/// </summary>
private void MakeMirroredPalette()
{
var prevPalette = _palette;
// important: create new array for palette
_palette = new byte[PaletteColors][];
for (var i = 0; i < PaletteColors; i++)
{
var rgb = prevPalette[PaletteColors - 1 - i];
_palette[i] = new [] { rgb[0], rgb[1], rgb[2] };
}
}
/// <summary>
/// Constructor (also calls copy constructor of the base ColorMap class)
///
/// We gently substitute ordinary palette with mirrored palette
///
/// </summary>
/// <param name="colorMap">Decorated colormap</param>
public MirrorColorMap(ColorMap colorMap) : base(colorMap)
{
MakeMirroredPalette();
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,55 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<MinimumVisualStudioVersion>10.0</MinimumVisualStudioVersion>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{CAA31C40-C298-4AB3-B022-3134A08BE626}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>SciColorMaps.Portable</RootNamespace>
<AssemblyName>SciColorMaps.Portable</AssemblyName>
<DefaultLanguage>en-US</DefaultLanguage>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<TargetFrameworkProfile>Profile92</TargetFrameworkProfile>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<!-- A reference to the entire .NET Framework is automatically included -->
</ItemGroup>
<ItemGroup>
<Compile Include="ColorMap.cs" />
<Compile Include="GrayColorMap.cs" />
<Compile Include="GrayScaleOptions.cs" />
<Compile Include="MirrorColorMap.cs" />
<Compile Include="Palette.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View File

@@ -1,4 +0,0 @@
ocean
cool
coolwarm
accent

File diff suppressed because it is too large Load Diff

View File

@@ -1,95 +0,0 @@
using System;
using System.Collections.Generic;
namespace SciColorMaps{%PORTABLE%}
{
#if !RECTANGULAR
/// <summary>
/// Class containing colormaps (palettes) imported from matplotlib
///
/// =========================== IMPORTANT NOTES! ==========================
///
/// 1)
/// Accessing elements in jagged arrays is significantly faster(up to 40%)
/// compared to rectangular arrays, so the following code is compiled by default.
/// If an efficient memory management is of more importance then compile
/// with the 'RECTANGULAR' conditional symbol.
///
/// 2)
/// Palette arrays are instantiated LAZILY in the calling code, e.g.:
/// var palette = Palette.GnuPlot.Value;
///
/// 3)
/// All palettes are READONLY arrays. That means they are NOT consts
/// and can be modified by calling code. It is AGAINST the contract,
/// however for the sake of performance there are currently no
/// additional checks or converting to ReadOnlyCollection in the code.
///
/// </summary>
static class Palette
{
/// <summary>
/// Number of base colors in palette
/// </summary>
public const int Resolution = {%RESOLUTION%};
#region palettes
{%COLORMAP%}
/// <summary>
/// Colormap "{%COLORMAP_NAME%}" taken from matplotlib
/// </summary>
public static readonly Lazy<byte[][]> {%COLORMAP_NAME_TITLE%} = new Lazy<byte[][]>(() => new []
{
{%BYTE[][]%}
});{%COLORMAP%}
#endregion
/// <summary>
/// Convenient dictionary for mapping palette names onto palette byte arrays
/// </summary>
public static readonly Dictionary<string, Lazy<byte[][]>>
ByName = new Dictionary<string, Lazy<byte[][]>>
{{%COLORMAP_PAIRS%}
};
/// <summary>
/// Enumerate all available palettes / colormaps
/// </summary>
public static IEnumerable<string> Names => ByName.Keys;
}
#else
public static class Palette
{
/// <summary>
/// Number of base colors in palette
/// </summary>
public const int Resolution = {%RESOLUTION%};
#region palettes
{%COLORMAP%}
/// <summary>
/// Colormap "{%COLORMAP_NAME%}" taken from matplotlib
/// </summary>
public static readonly Lazy<byte[,]> {%COLORMAP_NAME_TITLE%} = new Lazy<byte[,]>(() => new byte[,]
{
{%BYTE[,]%}
});{%COLORMAP%}
#endregion
/// <summary>
/// Convenient dictionary for mapping palette names onto palette byte arrays
/// </summary>
public static readonly Dictionary<string, Lazy<byte[,]>>
ByName = new Dictionary<string, Lazy<byte[,]>>
{{%COLORMAP_PAIRS%}
};
/// <summary>
/// Enumerate all available palettes / colormaps
/// </summary>
public static IEnumerable<string> Names => ByName.Keys;
}
#endif
}

View File

@@ -0,0 +1,22 @@
echo "module Matplotlib.ColorMaps.ColorMaps"
echo
for i in *.txt; do
cm=$(basename $i .txt)
echo "let $cm ="
echo " [|"
cat $i \
| sed -rn '/Jagged.*/,${s/new byte\[\] \{ *([0-9]+), *([0-9]+), *([0-9]+)\},? */ \1uy, \2uy, \3uy\n/gp}' \
| sed -r '/^$/d'
echo " |]"
echo
done
echo "let colormaps ="
echo " [|"
for i in *.txt; do
cm=$(basename $i .txt)
echo " \"$cm\", $cm"
done
echo " |] |> Map.ofArray"

74
src/ColorMap.fs Normal file
View File

@@ -0,0 +1,74 @@
namespace Matplotlib.ColorMaps
module ColorMap =
type ColorMap(palette: Palette, ?min: float, ?max: float) =
let lower = defaultArg min 0.0
let upper = defaultArg max 1.0
let nColors = palette.Length
let nColors' = float palette.Length
let colorRange = (upper - lower) / float nColors
do
if nColors <= 1 then
failwith "Number of colors should be larger than 1!"
if lower >= upper then
failwith "Upper bound should be greater than the lower one!"
new (name: string, ?min: float, ?max: float, ?numColors: int) =
let paletteName = name.ToLower()
let nColors = defaultArg numColors 256
let palette =
if Map.containsKey paletteName Palette16.palettes |> not then
failwith $"Unknown prefefined palette {name}"
else
ColorMap.makePalette (Palette16.palettes[paletteName], nColors)
let lower = defaultArg min 0.0
let upper = defaultArg max 1.0
ColorMap(palette, lower, upper)
member val PaletteName = "" with get, set
member x.Palette = palette
member x.color (v: float) =
let v = // clamp
if v < lower then
lower
elif v > upper then
upper
else v
let idx = (v - lower) / colorRange * nColors' |> int
palette[idx]
static member makePalette (colors: Palette, positions: float [], ?numColors) : Palette =
if positions[0] <> 0.0 || positions[^0] <> 1.0 then
failwith "First color position should be 0.0f and last position should be 1.0f!"
let nColors = defaultArg numColors 256 |> float
let groups = positions |> Array.map (fun pos -> pos * nColors |> int)
let c2d (r, g, b) = double r, double g, double b
groups |> Array.collect (fun idx ->
let r1, g1, b1 = colors[idx] |> c2d
let r2, g2, b2 = colors[idx + 1] |> c2d
let groupSize = groups[idx + 1] - groups[idx]
let groupSize' = double groupSize
[|
for j = 0 to groupSize do
let j' = double j / groupSize'
let r = (r1 + (r2 - r1) * j') |> byte
let g = (g1 + (g2 - g1) * j') |> byte
let b = (b1 + (b2 - b1) * j') |> byte
r, g, b
|]
)
static member makePalette (colors: Palette, ?numColors: int) : Palette =
let nPalette = defaultArg numColors 256
let nColors' = colors.Length |> float
let positions =
[| 0 .. nPalette - 1 |]
|> Array.map (fun pos -> float pos / nColors')
ColorMap.makePalette (colors, positions, nPalette)

75
src/GrayColorMap.fs Normal file
View File

@@ -0,0 +1,75 @@
namespace Matplotlib.ColorMaps
open System
module Gray =
// Three options for how to convert RGB color to grayscale
// (as used in GIMP: https://docs.gimp.org/2.6/en/gimp-tool-desaturate.html)
type GrayScaleOptions =
| Luminosity = 0
| Lightness = 1
| Average = 2
// ColorMap decorator that converts all colors of decorated colormap to grayscale
//
// Usage example:
//
// var cmap = new GrayColorMap(new ColorMap("viridis", -0.5, 0.5))
// ...
// var color = cmap[0.3]
//
// Important:
// Method allocates NEW memory for palette array
// so that it doesn't affect predefined palettes
//
// public class GrayColorMap : ColorMap
// // Grayscale conversion options
//
// private readonly GrayScaleOptions _options
//
// // Recalculate palette colors each open one of 3 simple formulae
// private void MakePaletteGrayscale()
//
// var prevPalette = _palette
//
// // important: create new array for palette
// _palette = new byte[PaletteColors][]
//
// Func<byte[], byte> convertToGray
//
// switch (_options)
//
// case GrayScaleOptions.Lightness:
// convertToGray = color => (byte)((color.Max() + color.Min()) / 2)
// break
// case GrayScaleOptions.Average:
// convertToGray = color => (byte)((color[0] + color[1] + color[2]) / 3)
// break
// default:
// convertToGray = color => (byte)(color[0] * 0.21 + color[1] * 0.72 + color[2] * 0.07)
// break
//
//
// for (var i = 0 i < PaletteColors; i++)
//
// var gray = convertToGray(prevPalette[i])
//
// _palette[i] = new [] gray, gray, gray
// Constructor (also calls copy constructor of the base ColorMap class)
//
// We gently substitute ordinary palette with grayscale palette
//
// public GrayColorMap(ColorMap colorMap,
// GrayScaleOptions options = GrayScaleOptions.Luminosity)
// : base(colorMap)
//
// _options = options
//
// // magic goes on here:
// MakePaletteGrayscale()

View File

@@ -1,5 +0,0 @@
module Matplotlib.ColorMaps
open System
let id a = a

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk.Web">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Library</OutputType>
<TargetFramework>net6.0</TargetFramework>
@@ -9,12 +9,15 @@
<Authors></Authors>
<Company></Company>
<Version>0.0.1</Version>
<LangVersion>preview</LangVersion>
</PropertyGroup>
<ItemGroup>
<Compile Include="Library.fs" />
<Compile Include="GrayColorMap.fs" />
<Compile Include="MirrorColorMap.fs" />
<Compile Include="Palette.fs" />
<Compile Include="ColorMap.fs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="FSharp.Data" Version="4.2.7" />
<PackageReference Include="FSharpPlus" Version="1.2.2" />
<Content Include="*.fsproj; *.fs" PackagePath="fable/" />
</ItemGroup>
</Project>

42
src/MirrorColorMap.fs Normal file
View File

@@ -0,0 +1,42 @@
namespace Matplotlib.ColorMaps
module Mirror =
// ColorMap decorator that mirrors colors of decorated colormap
//
// Usage example:
//
// var cmap = new MirrorColorMap(new ColorMap("ocean"))
// ...
// var color = cmap[0.3]
//
// Important:
// Method allocates NEW memory for palette array
// so that it doesn't affect predefined palettes
//
// public class MirrorColorMap : ColorMap
//
// // Recalculate palette colors
// private void MakeMirroredPalette()
//
// var prevPalette = _palette
//
// // important: create new array for palette
// _palette = new byte[PaletteColors][]
//
// for (var i = 0 i < PaletteColors; i++)
//
// var rgb = prevPalette[PaletteColors - 1 - i]
//
// _palette[i] = new [] rgb[0], rgb[1], rgb[2]
// Constructor (also calls copy constructor of the base ColorMap class)
//
// We gently substitute ordinary palette with mirrored palette
//
// public MirrorColorMap(ColorMap colorMap) : base(colorMap)
//
// MakeMirroredPalette()
let mirror = ()

1791
src/Palette.fs Normal file

File diff suppressed because it is too large Load Diff

19583
src/Palette256.fs Normal file

File diff suppressed because it is too large Load Diff

13
src/packages.lock.json Normal file
View File

@@ -0,0 +1,13 @@
{
"version": 1,
"dependencies": {
"net6.0": {
"FSharp.Core": {
"type": "Direct",
"requested": "[6.0.1, )",
"resolved": "6.0.1",
"contentHash": "VrFAiW8dEEekk+0aqlbvMNZzDvYXmgWZwAt68AUBqaWK8RnoEVUNglj66bZzhs4/U63q0EfXlhcEKnH1sTYLjw=="
}
}
}
}