Skip to content

Commit

Permalink
First code commit: forcolormap 0.8
Browse files Browse the repository at this point in the history
  • Loading branch information
vmagnin committed Oct 23, 2023
1 parent ec41426 commit 19d1b9c
Show file tree
Hide file tree
Showing 14 changed files with 7,921 additions and 0 deletions.
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
# Colormaps text files
*.lut

# Images
*.ppm

# Prerequisites
*.d

Expand Down
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Changelog
All notable changes to the gtk-fortran project are documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).


## [forcolormap 0.8] 2023-10-23

First code commit.
29 changes: 29 additions & 0 deletions NO_BIJECTION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# No bijection!

## The long way toward a theory of colors

Color is a complex and fascinating subject, partly because there is no bijection between wavelengths and colors, and a theory of colors can therefore not be purely physical. Each wavelength of the visible spectrum corresponds to a color, but colors can be [metamers](https://en.wikipedia.org/wiki/Metamerism_(color)) : for example yellow is in the spectrum (~580 nm) but can also be obtained my mixing essentially green and red. And all colors are not in the spectrum (brown, pink, etc.). A scientist could even pretend that black and white are not colors but an absence or a mix of colors, but a painter could see things differently. [Pierre Soulages](https://en.wikipedia.org/wiki/Pierre_Soulages) explored the reflections of light on black in its paintings.

There are around 1e8 photoreceptors in our eye. 95 % are rod cells and 5 % are [cone cells](https://en.wikipedia.org/wiki/Cone_cell) sensible to Short, Medium or Long wavelengths. Finally colors are built in our brain using information from the eye and we can distinguish ~300000 colors. If one or several types of cones don't work normally, a [color vision deficiency](https://en.wikipedia.org/wiki/Congenital_red%E2%80%93green_color_blindness) (CVD) is present. On the contrary, some people are also [synesthetes](https://en.wikipedia.org/wiki/Synesthesia) and see colors when they hear music, like the composer Olivier Messiaen. Some animals see the world in grey, others live in a more colored world than us: the [mantis shrimp](https://en.wikipedia.org/wiki/Mantis_shrimp) has 16 different cones!

It's no surprise that the way was long to understand colors: the young Isaac Newton (24 years old) worked on the visible spectrum with two prisms in 1666 as he was confined during the plague, Thomas Young postulated in 1802 the existence of three kinds of color receptors, Goethe was interested by the perception of colors (1810), Philipp Otto Runge published in 1810 its color sphere with its black and white poles, Maxwell published its triangle of colors in the middle of the 19th century and wrote the article "Experiments on colour, as perceived by the eye, with remarks on colour-blindness" (1855), Albert Henry Munsell published its color order system (Hue, Value, and Chroma) in 1915, etc.


## "Earth is as blue as an orange" (La terre est bleue comme une orange)

Finally, that Paul Eluard's statement starting a 1929 [poem](https://www.poetica.fr/poeme-868/paul-eluard-la-terre-est-bleue/) may not be as surrealist as he thought. And the young Arthur Rimbaud probably did not know that [grapheme–color synesthesia](https://en.wikipedia.org/wiki/Grapheme%E2%80%93color_synesthesia) does exist when he gave colors to the [vowels](https://en.wikipedia.org/wiki/Voyelles) in 1871:

> A black, E white, I red, U green, O blue, vowels,
> Some day I'll tell your latent births
> A noir, E blanc, I rouge, U vert, O bleu : voyelles,
> Je dirai quelque jour vos naissances latentes

In literature, colors can even be strange and frightening. [The Colour out of Space](https://www.hplovecraft.com/writings/texts/fiction/cs.aspx) is the title of a short story published in Amazing Stories in 1927 by H. P. Lovecraft:

> "and when upon heating before the spectroscope it displayed shining bands unlike any known colours of the normal spectrum there was much breathless talk of new elements, bizarre optical properties, and other things which puzzled men of science are wont to say when faced by the unknown."
> "It was just a colour—but not any colour of our earth or heavens."
114 changes: 114 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
# ForColormap

This small Fortran colormap fpm library is independent of any graphical toolkit: it just converts a real value to RGB values, that you can use with any toolkit offering bitmap drawing.

It includes a few basic colormaps, plus the Dave Green's [cubehelix](https://www.mrao.cam.ac.uk/~dag/CUBEHELIX/) colormap, plus 62 colormaps of the *Scientific colour maps* collection v8.0.1 by Fabio Crameri (the discrete palettes were not imported). See Fabio Crameri's poster ["Scientific Colour Maps"](https://www.fabiocrameri.ch/ws/media-library/a17d02961b3a4544961416de2d7900a4/posterscientificcolourmaps_crameri.pdf) for more information and my [No Bijection!](NO_BIJECTION.md) text about the mysteries and wonders of colors.


## Basic usage

Assuming your graphical library has a `setpixelgb()`-like function and z is in the [0, 2] range, you can write something like:

```fortran
use forcolormap
...
type(Colormap) :: cmap
integer :: red, green, blue
real(wp) :: z, x, y
...
call cmap%set("glasgow", 0.0_wp, 2.0_wp)
...
z = f(x,y)
call cmap%compute_RGB(z, red, green, blue)
call setpixelrgb(x, y, red, green, blue)
```

Look into the `example/demo.f90` file for more usage examples: you can create your own colormap, download a colormap from a text file, etc.

Note that **there is no default colormap** as we consider that each user must choose a colormap adapted to the properties of its data.


## Installation

### Requirements

You need:

* a modern Fortran compiler, for example GFortran or the Intel ifort/ifx compilers. See the [Fortran-lang.org compilers page](https://fortran-lang.org/compilers/) for other compilers.
* The Fortran Package Manager [fpm](https://fpm.fortran-lang.org/).
* Any operating system.

### Testing the project

If you have a GitHub account, just clone the repository and launch the demo example:

```bash
$ git clone [email protected]:vmagnin/forcolormap.git
$ cd forcolormap
$ fpm run --example
```

The demo is creating [PPM files](https://en.wikipedia.org/wiki/Netpbm#File_formats) with colormaps and colorbars for all the available colormaps.

### Using ForColormap as a fpm dependency

To use ForColormap within your own `fpm` project, add the following lines to your `fpm.toml` manifest file:

```toml
[dependencies]
forcolormap = {git = "https://github.com/vmagnin/forcolormap" }
```


## TODO / ideas for further developments

* [ ] Create a logo: inspired by Newton/Dark Side of the Moon? Or a rainbow? And using Fortran purple.
* [ ] Improve the documentation.
* [ ] Include a few images in the README.md file.
* [ ] In the *Scientific colour maps,* the discrete colormaps were not imported for the time being because there is no `.lut` file.
* [ ] The `set()` method could have an optional `reverse` option to reverse the color order in a palette.
* [ ] Colormaps could have an option for logscale.
* [ ] A `get_colorbar()` function could return an `array(:,:,1:3)` containing the RGB image of the colorbar. The arguments could be the width and height, the direction (horizontal/vertical), etc.
* [ ] A `save()` method could save a colormap as RGB values separated by spaces in a `.lut` text file.


## License

This project is under MIT license.


## Citing colormaps

* If you use one of the *Scientific colour maps,* please cite these two items:
* Crameri, F. (2018a), Scientific colour maps. Zenodo. http:https://doi.org/10.5281/zenodo.1243862
* Crameri, Fabio, Grace E. Shephard, and Philip J. Heron. “The Misuse of Colour in Science Communication.” Nature Communications 11, no. 1 (October 28, 2020): 5444. https://doi.org/10.1038/s41467-020-19160-7.
* If you use the *cubehelix* colormap, please cite:
* Green, D. A. “A Colour Scheme for the Display of Astronomical Intensity Images.” arXiv, August 30, 2011. http:https://arxiv.org/abs/1108.5083.
modern

## References

### Articles and books

* Nuñez, Jamie R., Christopher R. Anderton, and Ryan S. Renslow. “Optimizing Colormaps with Consideration for Color Vision Deficiency to Enable Accurate Interpretation of Scientific Data.” Edited by Jesús Malo. PLOS ONE 13, no. 7 (August 1, 2018): e0199239. https://doi.org/10.1371/journal.pone.0199239.
* Rogowitz, Bernice E, and Lloyd A Treinish. [“Why Should Engineers and Scientists Be Worried About Color?”](https://github.com/amadeusine/interesting-reads/blob/master/ibm-research__why-should-engineers-and-scientists-be-worried-about-color.pdf)
* Thyng, Kristen, Chad Greene, Robert Hetland, Heather Zimmerle, and Steven DiMarco. “True Colors of Oceanography: Guidelines for Effective and Accurate Colormap Selection.” Oceanography 29, no. 3 (September 1, 2016): 9–13. https://doi.org/10.5670/oceanog.2016.66.
* Valeur, Bernard. *La couleur dans tous ses éclats.* Bibliothèque scientifique. Paris: Belin-"Pour la science", 2011, ISBN 9782701158761.
* Valeur, Bernard. *Lumière et luminescence - Ces phénomènes lumineux qui nous entourent.* Bibliothèque scientifique. Paris: Belin-"Pour la science", 2005, ISBN 9782701136035.

### Web pages

#### About colormaps
* https://en.wikipedia.org/wiki/Color_gradient
* https://en.wikipedia.org/wiki/Heat_map
* Ken Hughes, ["Default colormaps: Are Parula and Viridis really an improvement over Jet?"](https://brushingupscience.com/2019/10/01/default-colormaps-are-parula-and-viridis-really-an-improvement-over-jet/), posted on October 1, 2019.
* "In Search of a Perfect Colormap", http:https://inversed.ru/Blog_2.htm

#### Specific colormaps
* https://www.mrao.cam.ac.uk/~dag/CUBEHELIX/
* Scientific colour maps (Fabio Crameri):
* https://www.fabiocrameri.ch/colourmaps/
* https://s-ink.org/colour-map-guideline
* https://s-ink.org/scientific-colour-maps
* [Colors for data scientists. Generate and refine palettes of optimally distinct colors.](https://medialab.github.io/iwanthue/)
* Nathaniel Smith and Stéfan van der Walt, *A Better Default Colormap for Matplotlib,* SciPy 2015: https://www.youtube.com/watch?v=xAoljeRJ3lU
66 changes: 66 additions & 0 deletions example/demo.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
! The MIT License (MIT)
!
! Copyright (c) 2023 Vincent Magnin
!
! 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.
!-------------------------------------------------------------------------------
! Contributed by vmagnin: 2023-09-26
! Last modification: vmagnin 2023-10-19
!-------------------------------------------------------------------------------

program demo
use forcolormap
implicit none

integer :: i
type(Colormap) :: cmap, custom_cmap
! A discrete colormap with 8 levels, from black to white:
integer, dimension(0:7, 3) :: my_colormap = reshape( [ &
0, 0, 0, &
255, 0, 0, &
0, 255, 0, &
0, 0, 255, &
255, 255, 0, &
0, 255, 255, &
255, 0, 255, &
255, 255, 255 ], &
shape(my_colormap), order = [2, 1] )

! Let's create PPM files for each built-in colormap.
! The built-in z=f(x,y) test function is in the [0, 2] range:
do i = 1, size(colormaps_list)
call cmap%set(trim(colormaps_list(i)), 0.0_wp, 2.0_wp)
call cmap%test()
print '("Colormap ", A30, " has ", I0, " levels")', trim(cmap%get_current()), cmap%get_levels()
end do

! Cubehelix can also accept other paramaters (varargs array):
call cmap%set("cubehelix", 0.0_wp, 2.0_wp, 1024, [0.5_wp, -1.0_wp, 1.0_wp, 1.0_wp])
! We change the name for the output test files:
call cmap%test("cubehelix_customized")

! You can create your own colormap defined in an array:
call custom_cmap%create("discrete", 0.0_wp, 2.0_wp, my_colormap)
call custom_cmap%test()

! Or you can download it from a .txt file:
call custom_cmap%load("test_map_to_load.txt", 0.0_wp, 2.0_wp)
call custom_cmap%test("a_loaded_colormap")
call custom_cmap%print()
end program demo
29 changes: 29 additions & 0 deletions fpm.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name = "forcolormap"
description= "A small Fortran colormap library"
version = "0.8.0"
license = "MIT"
author = "Vincent Magnin"
copyright = "Copyright 2023, Vincent Magnin"
maintainer = "https://github.com/vmagnin/forcolormap/issues"
homepage = "https://github.com/vmagnin/forcolormap"
categories = ["graphics"]
keywords = ["Fortran", "fpm", "colormap"]

[[example]]
name = "example"
source-dir = "example"
main = "demo.f90"

[build]
auto-executables = true
auto-tests = true
auto-examples = true
module-naming = false

[install]
library = false

[fortran]
implicit-typing = false
implicit-external = false
source-form = "free"
125 changes: 125 additions & 0 deletions scripts/copy-paste_code.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
! Scientific colour maps collection (Fabio Crameri):
case("acton")
call self%create("acton", self%zmin, self%zmax, acton)
case("actonS")
call self%create("actonS", self%zmin, self%zmax, actonS)
case("bamako")
call self%create("bamako", self%zmin, self%zmax, bamako)
case("bamakoS")
call self%create("bamakoS", self%zmin, self%zmax, bamakoS)
case("bam")
call self%create("bam", self%zmin, self%zmax, bam)
case("bamO")
call self%create("bamO", self%zmin, self%zmax, bamO)
case("batlowK")
call self%create("batlowK", self%zmin, self%zmax, batlowK)
case("batlowKS")
call self%create("batlowKS", self%zmin, self%zmax, batlowKS)
case("batlow")
call self%create("batlow", self%zmin, self%zmax, batlow)
case("batlowS")
call self%create("batlowS", self%zmin, self%zmax, batlowS)
case("batlowW")
call self%create("batlowW", self%zmin, self%zmax, batlowW)
case("batlowWS")
call self%create("batlowWS", self%zmin, self%zmax, batlowWS)
case("berlin")
call self%create("berlin", self%zmin, self%zmax, berlin)
case("bilbao")
call self%create("bilbao", self%zmin, self%zmax, bilbao)
case("bilbaoS")
call self%create("bilbaoS", self%zmin, self%zmax, bilbaoS)
case("broc")
call self%create("broc", self%zmin, self%zmax, broc)
case("brocO")
call self%create("brocO", self%zmin, self%zmax, brocO)
case("buda")
call self%create("buda", self%zmin, self%zmax, buda)
case("budaS")
call self%create("budaS", self%zmin, self%zmax, budaS)
case("bukavu")
call self%create("bukavu", self%zmin, self%zmax, bukavu)
case("cork")
call self%create("cork", self%zmin, self%zmax, cork)
case("corkO")
call self%create("corkO", self%zmin, self%zmax, corkO)
case("davos")
call self%create("davos", self%zmin, self%zmax, davos)
case("davosS")
call self%create("davosS", self%zmin, self%zmax, davosS)
case("devon")
call self%create("devon", self%zmin, self%zmax, devon)
case("devonS")
call self%create("devonS", self%zmin, self%zmax, devonS)
case("fes")
call self%create("fes", self%zmin, self%zmax, fes)
case("glasgow")
call self%create("glasgow", self%zmin, self%zmax, glasgow)
case("glasgowS")
call self%create("glasgowS", self%zmin, self%zmax, glasgowS)
case("grayC")
call self%create("grayC", self%zmin, self%zmax, grayC)
case("grayCS")
call self%create("grayCS", self%zmin, self%zmax, grayCS)
case("hawaii")
call self%create("hawaii", self%zmin, self%zmax, hawaii)
case("hawaiiS")
call self%create("hawaiiS", self%zmin, self%zmax, hawaiiS)
case("imola")
call self%create("imola", self%zmin, self%zmax, imola)
case("imolaS")
call self%create("imolaS", self%zmin, self%zmax, imolaS)
case("lajolla")
call self%create("lajolla", self%zmin, self%zmax, lajolla)
case("lajollaS")
call self%create("lajollaS", self%zmin, self%zmax, lajollaS)
case("lapaz")
call self%create("lapaz", self%zmin, self%zmax, lapaz)
case("lapazS")
call self%create("lapazS", self%zmin, self%zmax, lapazS)
case("lipari")
call self%create("lipari", self%zmin, self%zmax, lipari)
case("lipariS")
call self%create("lipariS", self%zmin, self%zmax, lipariS)
case("lisbon")
call self%create("lisbon", self%zmin, self%zmax, lisbon)
case("managua")
call self%create("managua", self%zmin, self%zmax, managua)
case("navia")
call self%create("navia", self%zmin, self%zmax, navia)
case("naviaS")
call self%create("naviaS", self%zmin, self%zmax, naviaS)
case("naviaW")
call self%create("naviaW", self%zmin, self%zmax, naviaW)
case("naviaWS")
call self%create("naviaWS", self%zmin, self%zmax, naviaWS)
case("nuuk")
call self%create("nuuk", self%zmin, self%zmax, nuuk)
case("nuukS")
call self%create("nuukS", self%zmin, self%zmax, nuukS)
case("oleron")
call self%create("oleron", self%zmin, self%zmax, oleron)
case("oslo")
call self%create("oslo", self%zmin, self%zmax, oslo)
case("osloS")
call self%create("osloS", self%zmin, self%zmax, osloS)
case("roma")
call self%create("roma", self%zmin, self%zmax, roma)
case("romaO")
call self%create("romaO", self%zmin, self%zmax, romaO)
case("tofino")
call self%create("tofino", self%zmin, self%zmax, tofino)
case("tokyo")
call self%create("tokyo", self%zmin, self%zmax, tokyo)
case("tokyoS")
call self%create("tokyoS", self%zmin, self%zmax, tokyoS)
case("turku")
call self%create("turku", self%zmin, self%zmax, turku)
case("turkuS")
call self%create("turkuS", self%zmin, self%zmax, turkuS)
case("vanimo")
call self%create("vanimo", self%zmin, self%zmax, vanimo)
case("vik")
call self%create("vik", self%zmin, self%zmax, vik)
case("vikO")
call self%create("vikO", self%zmin, self%zmax, vikO)
Loading

0 comments on commit 19d1b9c

Please sign in to comment.