ref: 549703b901b087653d06b5b760ff39f8b9fe8dff
dir: /sys/src/games/rotzoomer.c/
/*
* rotzoomer - an affinewarp demo
*/
#include <u.h>
#include <libc.h>
#include <thread.h>
#include <draw.h>
#include <mouse.h>
#include <keyboard.h>
#include <geometry.h>
typedef struct Strobos Strobos;
typedef struct Backlight Backlight;
struct Strobos
{
Point p;
Point txtsz;
Image *c;
Image *back;
Image *txtim;
char *txt;
Warp txtwp;
};
struct Backlight
{
Image *c; /* bag of colors */
Image *b; /* chosen one */
Warp w; /* roulette */
};
Rectangle UR = {0,0,1,1};
Image *bg;
Backlight *bl;
Image *sprite;
Image *screenb;
Mousectl *mctl;
Keyboardctl *kctl;
Channel *drawc;
Warp warp;
int nframes;
Strobos **strobostab;
int nstrobos;
int smooth;
void *
emalloc(ulong size)
{
void *p;
p = malloc(size);
if(p == nil)
sysfatal("malloc: %r");
setmalloctag(p, getcallerpc(&size));
return p;
}
Image *
eallocimage(Display *disp, Rectangle r, ulong chan, int repl, ulong col)
{
Image *i;
i = allocimage(disp, r, chan, repl, col);
if(i == nil)
sysfatal("allocimage: %r");
setmalloctag(i, getcallerpc(&disp));
return i;
}
Image *
allocbasketweave(void)
{
static uchar pattern[] = {
0x07, 0x8B, 0xDD, 0xB8,
0x70, 0xE8, 0xDD, 0x8E,
};
Image *i;
i = eallocimage(display, Rect(0,0,8,8), GREY1, 1, DNofill);
if(loadimage(i, i->r, pattern, 8) != 8)
sysfatal("loadimage: %r");
return i;
}
Backlight *
allocbacklight(void)
{
static ulong pattern[] = {
0x888888, 0xDDDDDD,
0xBBBBBB, 0xFFFFFF,
};
Backlight *b;
b = emalloc(sizeof(Backlight));
b->c = eallocimage(display, Rect(0,0,2,2), XRGB32, 1, DNofill);
b->b = eallocimage(display, UR, XRGB32, 1, DNofill);
if(loadimage(b->c, b->c->r, (uchar*)pattern, 2*2*4) != 2*2*4)
sysfatal("loadimage: %r");
return b;
}
void
drawstats(void)
{
static Point sp = {10,10};
char s[128];
snprint(s, sizeof s, "frames %d", nframes);
stringbg(screenb, sp, display->white, ZP, font, s, display->black, ZP);
}
Strobos *
allocstrobos(char *txt, ulong col)
{
Strobos *s;
s = emalloc(sizeof(Strobos));
s->c = eallocimage(display, UR, RGBA32, 1, col);
s->txt = txt;
s->txtsz = stringsize(font, txt);
s->txtim = eallocimage(display, Rpt(ZP, s->txtsz), RGBA32, 0, DTransparent);
string(s->txtim, ZP, s->c, ZP, font, txt);
s->back = eallocimage(display, Rpt(ZP, mulpt(s->txtsz, 4)), RGBA32, 0, DTransparent);
return s;
}
void
initstrobos(void)
{
static struct {
char *lbl;
ulong col;
} tab[] = {
"MAKE ME AN OFFER", DPaleyellow,
"ONE HUNDRED BILLION DOLLARS", DYellow,
"THE PRICEMASTER HAS SPOKEN", DDarkyellow,
};
int i;
nstrobos = nelem(tab);
strobostab = emalloc(nstrobos*sizeof(Strobos *));
for(i = 0; i < nstrobos; i++)
strobostab[i] = allocstrobos(tab[i].lbl, setalpha(tab[i].col, 0x7F));
}
void
drawstrobos(Strobos *s)
{
double ss, θ;
if(nframes % 75 == 0){ /* 2½ seconds */
s->p.x = 50 + frand()*(Dx(screenb->r)-50-50 - Dx(s->back->r));
s->p.y = 50 + frand()*(Dy(screenb->r)-50-50 - Dy(s->back->r));
}
if((nframes & 1) == 0){ /* 1/15 second */
ss = (frand() + 0.2)*(4/1.2);
θ = (frand()*10 - 10/2)*DEG;
Matrix S = {
ss, 0, 0,
0, ss, 0,
0, 0, 1,
}, R = {
cos(θ), -sin(θ), 0,
sin(θ), cos(θ), 0,
0, 0, 1,
}, T₀ = {
1, 0, -s->txtsz.x/2,
0, 1, -s->txtsz.y/2,
0, 0, 1,
}, T₁ = {
1, 0, s->txtsz.x/2,
0, 1, s->txtsz.y/2,
0, 0, 1,
};
mulm(R, T₀);
mulm(T₁, R);
mulm(S, T₁);
mkwarp(s->txtwp, S);
}
affinewarp(s->back, s->back->r, s->txtim, s->txtim->r.min, s->txtwp, smooth);
draw(screenb, rectaddpt(s->back->r, addpt(screenb->r.min, s->p)), s->back, nil, ZP);
}
void
redraw(void)
{
Rectangle cr0;
int i;
rlockdisplay(display);
affinewarp(bl->b, bl->b->r, bl->c, ZP, bl->w, 0);
draw(screenb, screenb->r, bl->b, bg, ZP);
cr0 = screenb->clipr;
replclipr(screenb, 0, insetrect(screenb->r, 50));
affinewarp(screenb, screenb->clipr, sprite, sprite->r.min, warp, smooth);
replclipr(screenb, 0, cr0);
drawstats();
for(i = 0; i < nstrobos; i++)
drawstrobos(strobostab[i]);
draw(screen, screen->r, screenb, nil, ZP);
flushimage(display, 1);
runlockdisplay(display);
}
void
drawproc(void*)
{
threadsetname("drawproc");
for(;;){
recv(drawc, nil);
redraw();
nframes++;
}
}
void
update(double f)
{
Point t;
double c, s, ss;
c = cos(f);
s = sin(f);
ss = sin(f*2 + 10);
t.x = s*Dx(sprite->r)/2;
t.y = 0;
Matrix R = {
c, -s, 0,
s, c, 0,
0, 0, 1,
}, S = {
ss, 0, 0,
0, ss, 0,
0, 0, 1,
}, T₀ = {
1, 0, -Dx(sprite->r)/2,
0, 1, -Dy(sprite->r)/2,
0, 0, 1,
}, T₁ = {
1, 0, Dx(sprite->r)/2,
0, 1, Dy(sprite->r)/2,
0, 0, 1,
}, T = {
1, 0, t.x,
0, 1, t.y,
0, 0, 1,
};
mulm(S, T₀);
mulm(R, S);
mulm(T₁, R);
mulm(T, T₁);
mkwarp(warp, T);
/* spin it! */
t.x = Dx(bl->b->r)/2;
t.y = Dy(bl->b->r)/2;
Matrix R₁ = {
c, -s, 0,
s, c, 0,
0, 0, 1,
};
T₀[0][2] = -t.x;
T₀[1][2] = -t.y;
mulm(R₁, T₀);
mkwarp(bl->w, R₁);
}
void
clkproc(void*)
{
uvlong t0, Δt;
threadsetname("clkproc");
for(;;){
t0 = nsec();
update(t0/1e9);
nbsend(drawc, nil);
Δt = nsec() - t0;
if(Δt < 33*1000*1000)
sleep(33 - Δt/1000000);
}
}
void
mouse(void)
{
}
void
key(Rune r)
{
switch(r){
case Kdel:
case 'q':
threadexitsall(nil);
}
}
void
resize(void)
{
static Point lastsz;
Point cursz;
lockdisplay(display);
if(getwindow(display, Refnone) < 0)
fprint(2, "can't reattach to window\n");
cursz = subpt(screen->r.max, screen->r.min);
if(!eqpt(cursz, lastsz)){
freeimage(screenb);
screenb = eallocimage(display, Rpt(ZP, cursz), screen->chan, 0, DBlack);
lastsz = cursz;
}
unlockdisplay(display);
nbsend(drawc, nil);
}
void
usage(void)
{
fprint(2, "usage: %s [-s]\n", argv0);
exits("usage");
}
void
threadmain(int argc, char *argv[])
{
Rune r;
ARGBEGIN{
case 's':
smooth++;
break;
default:
usage();
}ARGEND;
if(argc != 0)
usage();
setfcr(getfcr() &~ FPINVAL);
if(initdraw(nil, nil, "rotzoomer") < 0)
sysfatal("initdraw: %r");
if((mctl = initmouse(nil, screen)) == nil)
sysfatal("initmouse: %r");
if((kctl = initkeyboard(nil)) == nil)
sysfatal("initkeyboard: %r");
bl = allocbacklight();
bg = allocbasketweave();
sprite = display->image;
screenb = eallocimage(display, rectsubpt(screen->r, screen->r.min), screen->chan, 0, DBlack);
initstrobos();
unlockdisplay(display);
drawc = chancreate(sizeof(void*), 1);
if(drawc == nil)
sysfatal("chancreate: %r");
proccreate(drawproc, nil, mainstacksize);
proccreate(clkproc, nil, mainstacksize);
enum {MOUSE, RESIZE, KEY};
Alt a[] = {
{mctl->c, &mctl->Mouse, CHANRCV},
{mctl->resizec, nil, CHANRCV},
{kctl->c, &r, CHANRCV},
{nil, nil, CHANEND}
};
for(;;)
switch(alt(a)){
case MOUSE: mouse(); break;
case RESIZE: resize(); break;
case KEY: key(r); break;
}
}