ref: d63e7e5714f4f42a38372fcd57f56e0129fb4c14
dir: /pill/pill.c/
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <event.h>
#define STEPS 3600
#define RADIUS .60
#define MAXSATURATION 1.0
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define RAD2DEG(r_) ((180.0)/PI * (r_))
#define DEG2RAD(r_) (PI/(180.0) * (r_))
#define BACKGROUNDCOLOR 0x22272EFF
#define CIRCLESLICE (2*PI/5)
#define CIRCLESHIFT (PI/2 + CIRCLESLICE/2)
#define CIRCLEDIAMETER (2*PI - CIRCLESLICE)
#define COLORPICKERPADDING .10
#define COLORPICKERRADIUS .25
#define RECTANGLE(w, h) ((Rectangle){(Point){(0),(0)}, (Point){(w),(h)}})
Image * Circle = nil;
uint CircleBPL = 0;
u32int * RawCircle = nil;
uint RawCircleSize = 0;
Image * Background = nil;
float SaturationStart = .50;
float HueStart = PI*.35;
float Value = 1.0;
int RunApp = 1;
int UpdateCircle = 1;
static unsigned int
hsv2rgb(float h, float s, float v)
{
// Taken from Computer Graphics: Principle and Practice
unsigned int rgb;
float f, p, q, t, r = 0.0, g = 0.0, b = 0.0;
int i;
h = RAD2DEG(h);
h /= 60.0;
i = (int)floor(h);
f = h - i;
p = v * (1.0 - s);
q = v * (1.0 - (s * f));
t = v * (1.0 - (s * (1.0 - f)));
switch(i)
{
case 0:
r = v;
g = t;
b = p;
break;
case 1:
r = q;
g = v;
b = p;
break;
case 2:
r = p;
g = v;
b = t;
break;
case 3:
r = p;
g = q;
b = v;
break;
case 4:
r = t;
g = p;
b = v;
break;
case 5:
r = v;
g = p;
b = q;
break;
default:
break;
}
rgb = (0xff & (uint)(b*255));
rgb |= (0xff & (uint)(g*255)) << 8;
rgb |= (0xff & (uint)(r*255)) << 16;
return (rgb << 8) | 0xff;
}
static void
updatecircle(void)
{
float t, i, l, s, u, g, ct, st;
Point c, e, k;
int w;
uint h;
if (Circle != nil) {
freeimage(Circle);
}
Circle = allocimage(display, RECTANGLE(Dx(screen->r), Dy(screen->r)), RGBA32, 1, BACKGROUNDCOLOR);
if (Circle == nil)
sysfatal("initcanvas: %r");
if (RawCircle != nil) {
free(RawCircle);
}
CircleBPL = bytesperline(Circle->r, Circle->depth);
RawCircleSize = CircleBPL * Dy(Circle->r);
RawCircle = malloc(RawCircleSize);
unloadimage(Circle, Circle->r, (uchar *)RawCircle, RawCircleSize);
c.x = Dx(Circle->r)/2;
c.y = Dy(Circle->r)/2;
l = MIN(Dx(screen->r), Dy(screen->r));
l *= RADIUS/2.0;
i = 1/(l*2);
for (t = 0.0; t < CIRCLEDIAMETER; t += i) {
u = t/CIRCLEDIAMETER;
for (s = 0; s < l; s += 0.1) {
e.x = c.x + s * cos(t + CIRCLESHIFT);
e.y = c.y + s * sin(t + CIRCLESHIFT);
RawCircle[Dx(Circle->r) * e.y + e.x] = hsv2rgb(
HueStart + u,
(SaturationStart)+(s/l)*(MAXSATURATION - SaturationStart),
Value);
}
}
s = l * (1+COLORPICKERPADDING);
u = s + (l*COLORPICKERRADIUS);
i = 1/(u*2);
for (t = .0; t < 2*PI; t+=i) {
ct = cos(t);
st = sin(t);
k.x = c.x + s * ct;
k.y = c.y + s * st;
e.x = c.x + u * ct;
e.y = c.y + u * st;
if (e.x < k.x) {
w = e.x;
e.x = k.x;
k.x = w;
}
h = hsv2rgb(t, 1.0, 1.0);
for (g = s; g < u; g++) {
RawCircle[Dx(Circle->r) * (c.y + ((int)(g * st))) + (c.x + ((int)(g * ct)))] = h;
}
}
loadimage(Circle, Circle->r, (uchar *)RawCircle, RawCircleSize);
}
static void
drawcanvas(void)
{
if (UpdateCircle) {
updatecircle();
UpdateCircle = 0;
}
draw(screen, screen->r, Background, nil, ZP);
draw(screen, screen->r, Circle, nil, ZP);
}
void
setbackground(ulong col)
{
if (Background != nil)
freeimage(Background);
Background = allocimage(display, RECTANGLE(1, 1), RGBA32, 1, col);
if (Background == nil)
sysfatal("setbackground: %r");
}
static void
initcanvas(void)
{
UpdateCircle = 1;
setbackground(BACKGROUNDCOLOR);
drawcanvas();
}
void
eresized(int)
{
if(getwindow(display, Refnone) < 0)
sysfatal("getwindow: %r");
initcanvas();
}
void
main(int argc, char * argv[])
{
Event e;
USED(argc);
USED(argv);
if (initdraw(nil, nil, "pill") < 0)
sysfatal("initdraw: %r\n");
einit(Emouse | Ekeyboard);
initcanvas();
for (;RunApp;) {
switch(event(&e)) {
case Ekeyboard:
if (e.kbdc == 'q') {
RunApp = 0;
}
break;
}
}
}