#!/usr/bin/perl # Copyright 2007, Michael Allan. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Textbender Software"), to deal in the Textbender Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicence, and/or sell copies of the Textbender Software, and to permit persons to whom the Textbender Software is furnished to do so, subject to the following conditions: The preceding copyright notice and this permission notice shall be included in all copies or substantial portions of the Textbender Software. THE TEXTBENDER 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 TEXTBENDER SOFTWARE OR THE USE OR OTHER DEALINGS IN THE TEXTBENDER SOFTWARE. use strict; use warnings; =pod =head1 NAME spectrum-css - calculate and output a chromographic reference spectrum, for in-page.css =head1 SYNOPSIS (t=~/project/textbender/a/u/chromography; $t/_/spectrum-css >> $t/in-page.css; ) =head1 DESCRIPTION Outputs a multi-variate, cyclic spectrum through the HLS colour space. The spectrum comprises a single hue cycle in reverse, from blue through green and yellow, to orange, red and violet. Luminance and saturation cycle meanwhile at a higher, but equal, frequency; phased so that saturation peaks at low luminance. =head1 FAULTS The resulting chromographs may be inadequate to reveal a symmetric inversion that happens to map to a peak (or valley) of luminance. A chromograph cell on the incline will be nearly identical to its opposite on the decline; the only difference being a slight (maybe undetectable) difference of hue, depending on the distance between inverted genes. (See spectrum.xht#Tests.) The cause of this fault is the colour flatness of each cell. Although a luminance/saturation gradient is apparent among cells, and reverses itself between incline and decline, it does not extend *into* individual cells. (The spectrum inclines and declines like a stairway, with cells like flat steps; rather than a ramp, with tilted cells.) Swapping cells to the wrong sides of the peak does not affect the apparent gradient. In future, when the gradient is extended into cells (maybe using background images with CSS3's background-size property) this fault will be corrected. Then inversions across peaks will show up clearly as reverse-gradient (reverse-tilted) cells. =cut sub amplitude( $$$$ ); # $length, $min, $max, $shift sub amplitude_H( $$$$ ); # " my $spectral_index = 0; # initially my $spectral_length = 1 << 10; my $H_length = $spectral_length; my $H_min = 0; my $H_max = 359; my $H_shift = (1 - 0.6666); # blue, declining thence my $L_length = $spectral_length >> 4; my $L_min = 0.35; my $L_max = 0.98; my $L_shift = 0.5; my $S_length = $spectral_length >> 4; my $S_min = 0.05; my $S_max = 0.45; my $S_shift = 0; { use Graphics::ColorUtils qw( hls2rgb ); for( ;$spectral_index < $spectral_length; ++$spectral_index ) { my $H = amplitude_H( $H_length, $H_min, $H_max, $H_shift ); my $L = amplitude( $L_length, $L_min, $L_max, $L_shift ); my $S = amplitude( $S_length, $S_min, $S_max, $S_shift ); my $back_color = hls2rgb( $H,$L,$S ); my $fore_color; if( $L > 0.70 ) { $fore_color = '#000'; } else { $fore_color = '#fff'; } print ".textbender-a-u-chromography-RS-$spectral_index { background-color: $back_color; color: $fore_color; }\n"; } } sub amplitude( $$$$ ) { my $length = shift; my $min = shift, my $max = shift; my $shift = shift; my $half_length = $length / 2; my $index = ($spectral_index + $shift * $length) % $length; $index > $half_length and $index = $length - $index; # reflection back from max (effectively) my $amplitude = $index / $half_length; # [0.0 ... 1.0] return $min + $amplitude * ($max - $min); } sub amplitude_H( $$$$ ) # too difficult to define in terms of amplitude(), because it is already cyclic (needs no reflection), and because it goes in reverse (like reflection, but with no switch from unreflected to reflected) { my $length = shift; my $min = shift, my $max = shift; my $shift = shift; my $index = ($spectral_index + $shift * $length) % $length; my $amplitude = $index / $length; # [0.0 ... 1.0] $amplitude = $max - $amplitude * ($max - $min); $amplitude < 0 and $amplitude += $max; return $amplitude; }