{"id":5013,"date":"2019-08-09T03:30:40","date_gmt":"2019-08-09T08:30:40","guid":{"rendered":"https:\/\/justinparrtech.com\/JustinParr-Tech\/?p=5013"},"modified":"2019-08-09T03:51:25","modified_gmt":"2019-08-09T08:51:25","slug":"spectrum-generating-color-function-using-sine-waves","status":"publish","type":"post","link":"https:\/\/justinparrtech.com\/JustinParr-Tech\/spectrum-generating-color-function-using-sine-waves\/","title":{"rendered":"Spectrum-Generating Color Function Using Sine Waves"},"content":{"rendered":"<p>Using a color scale to visualize output makes it easy to understand data, and see patterns that aren&#8217;t intuitive.<\/p>\n<p>A simple 1 or 2-color scale is really simple to implement, but what if you want to use the whole spectrum?<\/p>\n<p><!--more--><\/p>\n<p>&nbsp;<\/p>\n\n<p>&nbsp;<\/p>\n<div id=\"ez-toc-container\" class=\"ez-toc-v2_0_84 counter-hierarchy ez-toc-counter ez-toc-custom ez-toc-container-direction\">\n<div class=\"ez-toc-title-container\"><p class=\"ez-toc-title\" style=\"cursor:inherit\">Table of Contents<\/p>\n<\/div><nav><ul class='ez-toc-list ez-toc-list-level-1 ' ><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-1\" href=\"https:\/\/justinparrtech.com\/JustinParr-Tech\/spectrum-generating-color-function-using-sine-waves\/#tldr-just-give-me-the-code\" >TL;DR:\u00a0 Just Give Me The Code<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-2\" href=\"https:\/\/justinparrtech.com\/JustinParr-Tech\/spectrum-generating-color-function-using-sine-waves\/#what-are-we-trying-to-accomplish\" >What Are We Trying to Accomplish?<\/a><ul class='ez-toc-list-level-3' ><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-3\" href=\"https:\/\/justinparrtech.com\/JustinParr-Tech\/spectrum-generating-color-function-using-sine-waves\/#segmented-approach\" >Segmented Approach<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-4\" href=\"https:\/\/justinparrtech.com\/JustinParr-Tech\/spectrum-generating-color-function-using-sine-waves\/#sine-waves\" >Sine Waves<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-5\" href=\"https:\/\/justinparrtech.com\/JustinParr-Tech\/spectrum-generating-color-function-using-sine-waves\/#mapping-sine-waves\" >Mapping Sine Waves<\/a><ul class='ez-toc-list-level-3' ><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-6\" href=\"https:\/\/justinparrtech.com\/JustinParr-Tech\/spectrum-generating-color-function-using-sine-waves\/#map-%e2%80%9cn%e2%80%9d-to-2%cf%80\" >Map &#8220;n&#8221; to 2\u03c0<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-7\" href=\"https:\/\/justinparrtech.com\/JustinParr-Tech\/spectrum-generating-color-function-using-sine-waves\/#fix-the-amplitude\" >Fix the Amplitude<\/a><ul class='ez-toc-list-level-4' ><li class='ez-toc-heading-level-4'><a class=\"ez-toc-link ez-toc-heading-8\" href=\"https:\/\/justinparrtech.com\/JustinParr-Tech\/spectrum-generating-color-function-using-sine-waves\/#scale-and-shift\" >Scale and Shift<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-4'><a class=\"ez-toc-link ez-toc-heading-9\" href=\"https:\/\/justinparrtech.com\/JustinParr-Tech\/spectrum-generating-color-function-using-sine-waves\/#clamping\" >Clamping<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-10\" href=\"https:\/\/justinparrtech.com\/JustinParr-Tech\/spectrum-generating-color-function-using-sine-waves\/#shifting-peak-amplitude\" >Shifting Peak Amplitude<\/a><ul class='ez-toc-list-level-4' ><li class='ez-toc-heading-level-4'><a class=\"ez-toc-link ez-toc-heading-11\" href=\"https:\/\/justinparrtech.com\/JustinParr-Tech\/spectrum-generating-color-function-using-sine-waves\/#utility-shift\" >Utility Shift<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-4'><a class=\"ez-toc-link ez-toc-heading-12\" href=\"https:\/\/justinparrtech.com\/JustinParr-Tech\/spectrum-generating-color-function-using-sine-waves\/#color-channels\" >Color Channels<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-13\" href=\"https:\/\/justinparrtech.com\/JustinParr-Tech\/spectrum-generating-color-function-using-sine-waves\/#one-last-cleanup-task\" >One Last Cleanup Task<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-14\" href=\"https:\/\/justinparrtech.com\/JustinParr-Tech\/spectrum-generating-color-function-using-sine-waves\/#putting-it-all-together\" >Putting it All Together<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-15\" href=\"https:\/\/justinparrtech.com\/JustinParr-Tech\/spectrum-generating-color-function-using-sine-waves\/#variants\" >Variants<\/a><ul class='ez-toc-list-level-3' ><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-16\" href=\"https:\/\/justinparrtech.com\/JustinParr-Tech\/spectrum-generating-color-function-using-sine-waves\/#use-a-portion-of-the-spectrum\" >Use a Portion of the Spectrum<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-17\" href=\"https:\/\/justinparrtech.com\/JustinParr-Tech\/spectrum-generating-color-function-using-sine-waves\/#in-reverse\" >In Reverse<\/a><\/li><\/ul><\/li><\/ul><\/nav><\/div>\n<h2><span class=\"ez-toc-section\" id=\"tldr-just-give-me-the-code\"><\/span>TL;DR:\u00a0 Just Give Me The Code<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<ul>\n<li>m:\u00a0 Maximum value of n<\/li>\n<li>n:\u00a0 Some number 0..m<\/li>\n<\/ul>\n<p>Here is the pseudocode that you can port to your favorite language:<\/p>\n<pre>f(n,m):\r\n    a=5\u03c0n\/3m + \u03c0\/2\r\n    r=sin(a) * 192 + 128\r\n    r=max(0,min(255,y))\r\n    g=sin(a - 2\u03c0\/3) * 192 + 128\r\n    g=max(0,min(255,y))\r\n    b=sin(a - 4\u03c0\/3) * 192 + 128\r\n    b=max(0,min(255,y))\r\n    return (((((0xFF &lt;&lt; 8) || r) &lt;&lt; 8) || g) &lt;&lt; 8) || b<\/pre>\n<p>Be sure to pre-calculate constants, such as 5\u03c0\/3, \u03c0\/2, 2\u03c0\/3, and 4\u03c0\/3<\/p>\n<p>The last line uses bit shifting and bitwise-or, so that you get a 32-bit color value in &#8220;ARGB&#8221; format:<\/p>\n<pre>0xaarrggbb<\/pre>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<h2><span class=\"ez-toc-section\" id=\"what-are-we-trying-to-accomplish\"><\/span>What Are We Trying to Accomplish?<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-5020\" src=\"https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-content\/uploads\/ColorFunction-Figures-01.png\" alt=\"\" width=\"600\" height=\"425\" srcset=\"https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-content\/uploads\/ColorFunction-Figures-01.png 600w, https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-content\/uploads\/ColorFunction-Figures-01-300x213.png 300w\" sizes=\"auto, (max-width: 600px) 100vw, 600px\" \/><\/p>\n<p>Given an integer value &#8220;n&#8221;, we want to build a function &#8220;f&#8221; that returns a 32-bit color value in &#8220;ARGB&#8221; format.<\/p>\n<ul>\n<li>For lower values of &#8220;n&#8221;, we want to return a color closer to Red<\/li>\n<li>As &#8220;n&#8221; increases, we want to return Yellow, Green, Cyan, Blue, and eventually Magenta<\/li>\n<li>We want r, g, and b color values from 0 to 255 (0x00 to 0xFF in hex)<\/li>\n<li>We want &#8220;f&#8221; to return a single 32-bit integer in the form:<br \/>\n0xFFrrggbb<\/p>\n<ul>\n<li>FF = Alpha (opaque)<\/li>\n<li>rr = Red channel value (0 to 255)<\/li>\n<li>gg = Green channel value (0 to 255)<\/li>\n<li>bb = Blue channel value (0 to 255)<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-5023\" src=\"https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-content\/uploads\/ColorFunction-Figures-02.png\" alt=\"\" width=\"600\" height=\"425\" srcset=\"https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-content\/uploads\/ColorFunction-Figures-02.png 600w, https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-content\/uploads\/ColorFunction-Figures-02-300x213.png 300w\" sizes=\"auto, (max-width: 600px) 100vw, 600px\" \/><\/p>\n<p>We want our function to gradually shift from one color to another.<\/p>\n<p>Viewed as a graph:<\/p>\n<ul>\n<li>We start with red at maximum<\/li>\n<li>As &#8220;n&#8221; increases, we ramp up green until green hits full intensity (Yellow)<\/li>\n<li>Then, red ramps down to zero, with green still at full intensity (Green)<\/li>\n<li>As red hits zero intensity, blue begins to ramp up to full intensity, with green still at full (Cyan)<\/li>\n<li>Green then ramps down to zero, leaving blue high (Blue)<\/li>\n<li>And finally, red ramps up to full intensity with blue still high (Magenta)<\/li>\n<\/ul>\n<p>&nbsp;<\/p>\n<h3><span class=\"ez-toc-section\" id=\"segmented-approach\"><\/span>Segmented Approach<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-5024\" src=\"https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-content\/uploads\/ColorFunction-Figures-03.png\" alt=\"\" width=\"600\" height=\"425\" srcset=\"https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-content\/uploads\/ColorFunction-Figures-03.png 600w, https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-content\/uploads\/ColorFunction-Figures-03-300x213.png 300w\" sizes=\"auto, (max-width: 600px) 100vw, 600px\" \/><\/p>\n<p>In a segmented approach:<\/p>\n<ul>\n<li>We assume there are 256 * 5 discreet colors (1,280)<\/li>\n<li>We map n to a value in 0..1279, and we call this &#8220;b&#8221;<\/li>\n<li>We figure out which segment by dividing b by 256<\/li>\n<li>The segment number (0 to 5) gives us a set of rules for generating r, g, and b values<\/li>\n<li>We use the rules, along with the remainder &#8220;q&#8221; to generate r, g, and b<\/li>\n<\/ul>\n<p>From a code perspective, this is clunky and inefficient, but it can be done with only integer operations, making it relatively fast.<\/p>\n<p>But it&#8217;s not very fun&#8230;<\/p>\n<p>&nbsp;<\/p>\n<h2><span class=\"ez-toc-section\" id=\"sine-waves\"><\/span>Sine Waves<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-5025\" src=\"https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-content\/uploads\/ColorFunction-Figures-04.png\" alt=\"\" width=\"600\" height=\"425\" srcset=\"https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-content\/uploads\/ColorFunction-Figures-04.png 600w, https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-content\/uploads\/ColorFunction-Figures-04-300x213.png 300w\" sizes=\"auto, (max-width: 600px) 100vw, 600px\" \/><\/p>\n<p>If we look at just the green channel as an example, we see that it rises and falls, and because of its cyclic nature, we can overly a sine wave.<\/p>\n<p>Let&#8217;s look at the anatomy of a sine wave.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-5027\" src=\"https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-content\/uploads\/ColorFunction-Figures-05.png\" alt=\"\" width=\"600\" height=\"425\" srcset=\"https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-content\/uploads\/ColorFunction-Figures-05.png 600w, https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-content\/uploads\/ColorFunction-Figures-05-300x213.png 300w\" sizes=\"auto, (max-width: 600px) 100vw, 600px\" \/><\/p>\n<p>Sine waves fluctuate between +1 and -1 as a function of a specified angle.<\/p>\n<p>The distance around a circle is 2 * \u03c0 * radius, or 2\u03c0r.<\/p>\n<p>If we consider a &#8220;unit circle&#8221; centered at (0,0) whose radius is 1, then the distance around the circle is simply 2\u03c0, and we can use a portion of this distance to measure angular distance.<\/p>\n<ul>\n<li>At 0 degrees, we start to the right of the circle&#8217;s center, at (1,0).<\/li>\n<li>As we move counterclockwise,the top of the circle is \u03c0\/2 radians, at (0,1).<\/li>\n<li>The left side of the circle (half-way around) is \u03c0 radians, at (-1,0).<\/li>\n<li>The bottom of the circle is 3\u03c0\/2 radians (0,-1).<\/li>\n<li>As we continue to move counterclockwise, we reach 2\u03c0 radians, but this is also our starting point at 0 radians.<\/li>\n<\/ul>\n<p>Throughout this process, the sine function describes the &#8220;y&#8221; coordinate:<\/p>\n<ul>\n<li>0 at 0 degrees (and 2\u03c0 degrees)<\/li>\n<li>+1 at \u03c0\/2 degrees<\/li>\n<li>0 at \u03c0 degrees<\/li>\n<li>-1 at at 3\u03c0\/2 degrees<\/li>\n<\/ul>\n<p>&nbsp;<\/p>\n<h2><span class=\"ez-toc-section\" id=\"mapping-sine-waves\"><\/span>Mapping Sine Waves<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-5029\" src=\"https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-content\/uploads\/ColorFunction-Figures-06.png\" alt=\"\" width=\"600\" height=\"425\" srcset=\"https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-content\/uploads\/ColorFunction-Figures-06.png 600w, https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-content\/uploads\/ColorFunction-Figures-06-300x213.png 300w\" sizes=\"auto, (max-width: 600px) 100vw, 600px\" \/><\/p>\n<p>We have three things to solve, in order to utilize sine waves:<\/p>\n<ol>\n<li>Map our arbitrary integer &#8220;n&#8221; to the interval of 0..2\u03c0<\/li>\n<li>Scale, shift, and clamp the amplitude, so that the function returns 0..255 (our RGB channels must have a value in this range)<\/li>\n<li>Shift the peak amplitude left or right to align with each channel<\/li>\n<\/ol>\n<p>&nbsp;<\/p>\n<h3><span class=\"ez-toc-section\" id=\"map-%e2%80%9cn%e2%80%9d-to-2%cf%80\"><\/span>Map &#8220;n&#8221; to 2\u03c0<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-5031\" src=\"https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-content\/uploads\/ColorFunction-Figures-07.png\" alt=\"\" width=\"600\" height=\"425\" srcset=\"https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-content\/uploads\/ColorFunction-Figures-07.png 600w, https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-content\/uploads\/ColorFunction-Figures-07-300x213.png 300w\" sizes=\"auto, (max-width: 600px) 100vw, 600px\" \/><\/p>\n<p>The first task is to map any arbitrary value of &#8220;n&#8221; to a point on the unit circle, from 0 radians to 2\u03c0 radians.<\/p>\n<p>This is actually fairly simple to accomplish:<\/p>\n<ul>\n<li>We assume &#8220;m&#8221; is the maximum value of &#8220;n&#8221;, and is passed as a parameter to our function.<\/li>\n<li>If we divide n\/m, we get a number in the range 0..1.\n<ul>\n<li>As n approaches 0, n\/m approaches 0.<\/li>\n<li>As n approaches m, n\/m\u00a0 approaches 1.<\/li>\n<\/ul>\n<\/li>\n<li>We then multiply n\/m by the total distance around our unit circle, which is 2\u03c0.<br \/>\na=2\u03c0n\/m<\/li>\n<li>The result is an angle that we can use for the sine function.<\/li>\n<\/ul>\n<p>&nbsp;<\/p>\n<h3><span class=\"ez-toc-section\" id=\"fix-the-amplitude\"><\/span>Fix the Amplitude<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>The next step is to adjust the amplitude so that our function returns a useful value.<\/p>\n<p>The sine function returns a value from -1 to +1, but we require a value in the range 0..255.<\/p>\n<p>In order to get a useful value, we will need to scale the sine wave so that its amplitude is a useful height, shift it so that the part we want is above the zero line, and clamp the output so that we don&#8217;t exceed the 0..255 range.<\/p>\n<p>&nbsp;<\/p>\n<h4><span class=\"ez-toc-section\" id=\"scale-and-shift\"><\/span>Scale and Shift<span class=\"ez-toc-section-end\"><\/span><\/h4>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-5032\" src=\"https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-content\/uploads\/ColorFunction-Figures-08.png\" alt=\"\" width=\"600\" height=\"425\" srcset=\"https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-content\/uploads\/ColorFunction-Figures-08.png 600w, https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-content\/uploads\/ColorFunction-Figures-08-300x213.png 300w\" sizes=\"auto, (max-width: 600px) 100vw, 600px\" \/><\/p>\n<p>The first step is to scale the amplitude.<\/p>\n<p>Keep in mind that the range of sine(n) is 2 &#8211; it goes from -1 to +1.<\/p>\n<p>Therefore, any value that we use to scale the range will result in a range of double that value.<\/p>\n<p>For example, if we multiply the sine output by 127, the resulting range would be -127 to +127.\u00a0 Although this would give us 255 effective values, each channel would only reach full intensity at one specific point, and lowest intensity at one specific point.<\/p>\n<p>Looking back at what we want our function to do, we actually want each channel to have full intensity or lowest intensity for a greater range than just one point.<\/p>\n<p>If we multiply by 192, the range -192 to +192 gives us more values than we need (384 compared to 256), and we can use clamping (later) to create a plateau at the top and bottom that more closely matches our desired profile.<\/p>\n<p>The next step is to shift the part of the wave we need above the zero line:<\/p>\n<pre>y=sin(a) * 192 + 128<\/pre>\n<p>Before clamping, &#8220;y&#8221; will now be in the range of -64 to +320.<\/p>\n<p>&nbsp;<\/p>\n<h4><span class=\"ez-toc-section\" id=\"clamping\"><\/span>Clamping<span class=\"ez-toc-section-end\"><\/span><\/h4>\n<p>The last thing we need to do to the amplitude is to &#8220;clamp&#8221; (limit) the range to useful values.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-5033\" src=\"https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-content\/uploads\/ColorFunction-Figures-09.png\" alt=\"\" width=\"600\" height=\"425\" srcset=\"https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-content\/uploads\/ColorFunction-Figures-09.png 600w, https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-content\/uploads\/ColorFunction-Figures-09-300x213.png 300w\" sizes=\"auto, (max-width: 600px) 100vw, 600px\" \/><\/p>\n<p>In the graph above, our function returns values along the yellow line (-64 to +320), but we want to &#8220;clamp&#8221; or limit the returned value to the range between the green lines, which is 0 to 255.<\/p>\n<p>Effectively, we want values &lt; 0 to be clamped to 0, and values &gt; 255 to be clamped to 255.<\/p>\n<p>Unfortunately, the easiest way to do this is to use IF commands:<\/p>\n<pre>if(y&lt;0) y=0\r\nif(y&gt;255) y=255<\/pre>\n<p>Some languages support the conditional assignment operator:<\/p>\n<pre>y=(y&lt;0)?0:((y&gt;255)?255:y)<\/pre>\n<p>This effectively says:<\/p>\n<pre>if(y&lt;0)\r\n    y=0\r\nelse\r\n    if(y&gt;255)\r\n        y=255\r\n    else\r\n        y=y<\/pre>\n<p>The most effective way to clamp a value is to use the &#8220;min&#8221; and &#8220;max&#8221; functions, because this is what they are designed to do.\u00a0 However, not every language supports them:<\/p>\n<pre>y=max(0,min(255,y))<\/pre>\n<p>If y&gt;255, &#8220;min&#8221; clamps it to 255.\u00a0 If the output of min &lt; 0, &#8220;max&#8221; clamps it to 0.<\/p>\n<p>&nbsp;<\/p>\n<h3><span class=\"ez-toc-section\" id=\"shifting-peak-amplitude\"><\/span>Shifting Peak Amplitude<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-5035\" src=\"https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-content\/uploads\/ColorFunction-Figures-10.png\" alt=\"\" width=\"600\" height=\"425\" srcset=\"https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-content\/uploads\/ColorFunction-Figures-10.png 600w, https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-content\/uploads\/ColorFunction-Figures-10-300x213.png 300w\" sizes=\"auto, (max-width: 600px) 100vw, 600px\" \/><\/p>\n<p>Before we go on, let&#8217;s take a pause.<\/p>\n<ul>\n<li>Our function f(n,m) will return a 32-bit color based on how close n is to m<\/li>\n<li>We&#8217;ve mapped all possible values of n to the unit circle, based on m being congruent to 2\u03c0<\/li>\n<li>We&#8217;ve scaled the amplitude to a useful range, moved the critical portion above the zero line, and clamped the output<\/li>\n<\/ul>\n<p>The last thing we need to do is align the peak of our sine function with each of the three RGB channels.<\/p>\n<p>&nbsp;<\/p>\n<h4><span class=\"ez-toc-section\" id=\"utility-shift\"><\/span>Utility Shift<span class=\"ez-toc-section-end\"><\/span><\/h4>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-5036\" src=\"https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-content\/uploads\/ColorFunction-Figures-11.png\" alt=\"\" width=\"600\" height=\"425\" srcset=\"https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-content\/uploads\/ColorFunction-Figures-11.png 600w, https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-content\/uploads\/ColorFunction-Figures-11-300x213.png 300w\" sizes=\"auto, (max-width: 600px) 100vw, 600px\" \/><\/p>\n<p>The first thing we need to do is line up our sine wave so that its peak is in a useful position.<\/p>\n<p>Our sine wave starts off with y=0, and at \u03c0\/2, y=+1.<\/p>\n<p>However, our first channel, red, starts high (+1) and then goes to 0.<\/p>\n<p>We can shift the entire wave by adding \u03c0\/2 to the angle before we apply sine.<\/p>\n<p>&nbsp;<\/p>\n<h4><span class=\"ez-toc-section\" id=\"color-channels\"><\/span>Color Channels<span class=\"ez-toc-section-end\"><\/span><\/h4>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-5038\" src=\"https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-content\/uploads\/ColorFunction-Figures-12.png\" alt=\"\" width=\"600\" height=\"425\" srcset=\"https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-content\/uploads\/ColorFunction-Figures-12.png 600w, https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-content\/uploads\/ColorFunction-Figures-12-300x213.png 300w\" sizes=\"auto, (max-width: 600px) 100vw, 600px\" \/><\/p>\n<p>Each channel takes up 1\/3 of the spectrum, which is spread across the unit circle whose circumference is 2\u03c0.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-5040\" src=\"https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-content\/uploads\/ColorFunction-Figures-13.png\" alt=\"\" width=\"600\" height=\"425\" srcset=\"https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-content\/uploads\/ColorFunction-Figures-13.png 600w, https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-content\/uploads\/ColorFunction-Figures-13-300x213.png 300w\" sizes=\"auto, (max-width: 600px) 100vw, 600px\" \/><\/p>\n<p>After our utility shift, red starts off high at 0, so we need green to peak 1\/3 of the way past that, or 2\u03c0\/3.<\/p>\n<p>And, blue peaks at 2\/3 past that, or 2*2\u03c0\/3, or 4\u03c0\/3.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-5041\" src=\"https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-content\/uploads\/ColorFunction-Figures-14.png\" alt=\"\" width=\"600\" height=\"425\" srcset=\"https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-content\/uploads\/ColorFunction-Figures-14.png 600w, https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-content\/uploads\/ColorFunction-Figures-14-300x213.png 300w\" sizes=\"auto, (max-width: 600px) 100vw, 600px\" \/><\/p>\n<p>If we take our angle &#8220;a&#8221;, we can feed it in to three different versions of our sine function, each shifted by 1\/3.<\/p>\n<pre>r=sin(a)*192+128\r\ng=sin(a - 2\u03c0\/3)*192+128\r\nb=sin(a - 4\u03c0\/3)*192+128<\/pre>\n<p>&nbsp;<\/p>\n<h3><span class=\"ez-toc-section\" id=\"one-last-cleanup-task\"><\/span>One Last Cleanup Task<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-5043\" src=\"https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-content\/uploads\/ColorFunction-Figures-15.png\" alt=\"\" width=\"600\" height=\"425\" srcset=\"https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-content\/uploads\/ColorFunction-Figures-15.png 600w, https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-content\/uploads\/ColorFunction-Figures-15-300x213.png 300w\" sizes=\"auto, (max-width: 600px) 100vw, 600px\" \/><\/p>\n<p>At this point, we&#8217;re basically done.<\/p>\n<p>However, in the graph above, we see the spectrum loop from red (0 degrees) all the way back around to red (2\u03c0 degrees).<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-5044\" src=\"https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-content\/uploads\/ColorFunction-Figures-16.png\" alt=\"\" width=\"600\" height=\"425\" srcset=\"https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-content\/uploads\/ColorFunction-Figures-16.png 600w, https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-content\/uploads\/ColorFunction-Figures-16-300x213.png 300w\" sizes=\"auto, (max-width: 600px) 100vw, 600px\" \/><\/p>\n<p>The last 1\/6 goes from magenta (red and blue both high) to red, as blue drops to zero, and we don&#8217;t want any duplicate or ambiguous color values.<\/p>\n<p>To eliminate this last portion, we subtract it before we scale n, so we subtract 1\/6 of 2\u03c0.\u00a0 2\u03c0\/6 = \u03c0\/3.<\/p>\n<pre>a=(2\u03c0 - \u03c0\/3)n\/m + \u03c0\/2<\/pre>\n<p>Which simplifies to:<\/p>\n<pre>a=5\u03c0n\/3m + \u03c0\/2<\/pre>\n<p>&nbsp;<\/p>\n<h3><span class=\"ez-toc-section\" id=\"putting-it-all-together\"><\/span>Putting it All Together<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-5045\" src=\"https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-content\/uploads\/ColorFunction-Figures-17.png\" alt=\"\" width=\"600\" height=\"425\" srcset=\"https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-content\/uploads\/ColorFunction-Figures-17.png 600w, https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-content\/uploads\/ColorFunction-Figures-17-300x213.png 300w\" sizes=\"auto, (max-width: 600px) 100vw, 600px\" \/><\/p>\n<p>&#8230;and here is our finished function!<\/p>\n<p>To convert the r, g, and b values to a 32 bit ARGB, we use some fancy bit shifting:<\/p>\n<pre>We start with 0xFF, that will become the alpha value for fully-opaque.\r\n&lt;&lt; is the bit shift operator.\r\n\r\n0xFF &lt;&lt; 8 = 0xFF00\r\n\r\n|| is the bitwise-or operator.\r\n\r\n0xFF00 || r = 0xFFrr\r\n\r\n0xFFrr &lt;&lt; 8 = 0xFFrr00\r\n\r\n0xFFrr00 || g = 0xFFrrgg\r\n\r\n0xFFrrgg &lt;&lt; 8 = 0xFFrrgg00\r\n\r\n0xFFrrgg00 || b = <strong>0xFFrrggbb<\/strong><\/pre>\n<p>&nbsp;<\/p>\n<h2><span class=\"ez-toc-section\" id=\"variants\"><\/span>Variants<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>There are a couple of useful variants.<\/p>\n<p>&nbsp;<\/p>\n<h3><span class=\"ez-toc-section\" id=\"use-a-portion-of-the-spectrum\"><\/span>Use a Portion of the Spectrum<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-5046\" src=\"https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-content\/uploads\/ColorFunction-Figures-18.png\" alt=\"\" width=\"600\" height=\"425\" srcset=\"https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-content\/uploads\/ColorFunction-Figures-18.png 600w, https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-content\/uploads\/ColorFunction-Figures-18-300x213.png 300w\" sizes=\"auto, (max-width: 600px) 100vw, 600px\" \/><\/p>\n<p>To use just a portion of the spectrum, we need to know two things:<\/p>\n<ul>\n<li>Width:\u00a0 What portion do we map to n<\/li>\n<li>Offset:\u00a0 Where do we start (0 position)<\/li>\n<\/ul>\n<p>The position corresponding to 0, p(0), is our Offset.<\/p>\n<p>If we subtract our maximum position p(m) from p(0), we get our width.<\/p>\n<p>In the example above, we want to return a color in the range of green to blue, so our starting point, p(0) is the point corresponding to green, 2\u03c0\/3.\u00a0 Our ending point, p(m), is the point corresponding to blue, 4\u03c0\/3.<\/p>\n<p>Therefore our width is 4\u03c0\/3 &#8211; 2\u03c0\/3, which is 2\u03c0\/3.<\/p>\n<p>To get the shortened portion of the spectrum, we change our mapping of n, using our new values:<\/p>\n<pre>a=width * n\/m + \u03c0\/2 + offset<\/pre>\n<p>In this example:<\/p>\n<pre>a=2\u03c0\/3 * n\/m + \u03c0\/2 + 2\u03c0\/3<\/pre>\n<p>&nbsp;<\/p>\n<h3><span class=\"ez-toc-section\" id=\"in-reverse\"><\/span>In Reverse<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Let&#8217;s say you want the colors to appear in reverse &#8211; in our example above, let&#8217;s say you want green for larger values of n, and blue for smaller values.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-5048\" src=\"https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-content\/uploads\/ColorFunction-Figures-19.png\" alt=\"\" width=\"600\" height=\"425\" srcset=\"https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-content\/uploads\/ColorFunction-Figures-19.png 600w, https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-content\/uploads\/ColorFunction-Figures-19-300x213.png 300w\" sizes=\"auto, (max-width: 600px) 100vw, 600px\" \/><\/p>\n<p>This is easy to accomplish by using m to complement n:<\/p>\n<pre>(m-n)\/m<\/pre>\n<p>Where n\/m returns 0 as n approaches 0, and 1 as n approaches m, complementing n will result in the opposite.<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Using a color scale to visualize output makes it easy to understand data, and see patterns that aren&#8217;t intuitive. A simple 1 or 2-color scale is really simple to implement, but what if you want to use the whole spectrum?<\/p>\n","protected":false},"author":16,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[20],"tags":[],"class_list":["post-5013","post","type-post","status-publish","format-standard","hentry","category-science"],"_links":{"self":[{"href":"https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-json\/wp\/v2\/posts\/5013","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-json\/wp\/v2\/users\/16"}],"replies":[{"embeddable":true,"href":"https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-json\/wp\/v2\/comments?post=5013"}],"version-history":[{"count":10,"href":"https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-json\/wp\/v2\/posts\/5013\/revisions"}],"predecessor-version":[{"id":5054,"href":"https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-json\/wp\/v2\/posts\/5013\/revisions\/5054"}],"wp:attachment":[{"href":"https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-json\/wp\/v2\/media?parent=5013"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-json\/wp\/v2\/categories?post=5013"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-json\/wp\/v2\/tags?post=5013"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}