added games/timmy

front
aiju 2016-06-23 21:12:06 +02:00
parent baf20a548b
commit 4821c261c4
9 changed files with 1183 additions and 0 deletions

51
sys/man/1/timmy Normal file
View File

@ -0,0 +1,51 @@
.TH TIMMY 1
.SH NAME
timmy \- physics sandbox
.SH SYNOPSIS
.B games/timmy
[
.B -s
.I steps-per-frame
]
.SH DESCRIPTION
.I Timmy
is a simple 2D physics sandbox.
.PP
To pick up an object click on it with the LMB.
New objects can be created by picking up their archetypes in the gray area on the bottom (the "tray").
To place an object in the working area click at the desired position with the LMB;
.I timmy
will refuse to place the object if it would collide with an existing one.
To abort the process \(em deleting the carried object \(em click anywhere with the RMB.
Picking up an object in the working area with the RMB will duplicate the object.
.PP
The following operations can be performed with the keyboard.
.IP w
Rotate carried object by 15° to the left.
.IP e
Rotate carried object by 15° to the right.
.IP space
Start or stop the simulation.
.IP del
Exit timmy.
.PP
The small circles on some objects are "hinges".
Two hinges can be connected by placing them on top of each other.
Their relative position will not change during the simulation; objects are however free to rotate around them.
To undo a hinge, pick up either of the objects.
.PP
The
.B -s
option adjusts the speed of the simulation; only integer values are permitted.
It does not compromise accuracy.
.SH SOURCE
.B /sys/src/games/timmy
.SH BUGS
.IR Timmy 's
physics may occasionally appear to originate from another universe.
.PP
.B -s
is a hack.
.SH HISTORY
.I Timmy
first appeared in 9front (June, 2016).

56
sys/src/games/timmy/dat.h Normal file
View File

@ -0,0 +1,56 @@
typedef struct Hinge Hinge;
typedef struct ObjT ObjT;
typedef struct Obj Obj;
typedef struct Poly Poly;
typedef struct Vec Vec;
struct Vec {
double x, y;
};
struct Poly {
int nvert;
Vec *vert;
double invI;
};
struct Hinge {
Vec p;
Vec p0;
Obj *o;
Hinge *onext;
Hinge *cnext, *cprev;
Hinge *anext;
};
struct ObjT {
int flags;
Poly *poly;
Hinge *hinge;
Image *line, *fill;
double imass;
void (*draw)(Obj *, Image *);
void (*move)(Obj *, double, double, double);
void (*init)(Obj *);
};
struct Obj {
ObjT *tab;
Vec p, v;
double θ, ω;
Rectangle bbox;
Poly *poly;
Hinge *hinge;
Obj *next, *prev;
int idx;
};
enum {
TrayH = 100,
TraySpc = 20,
};
#define DEG 0.01745329251994330
#define Slop 0.5
#define HingeSep 4.0

25
sys/src/games/timmy/fns.h Normal file
View File

@ -0,0 +1,25 @@
void *emalloc(ulong);
Image *rgb(u32int);
Poly *mkpoly(int, ...);
Poly *polydup(Poly *);
void polytrans(Poly *, Poly *, double, double, double);
void polydraw(Poly *, Image *, Image *, Image *);
void polybbox(Poly *, Rectangle *);
void polyfix(Poly *);
Obj *mkobj(ObjT *);
Obj *objdup(Obj *);
void objcat(Obj *, Obj *);
Vec vecadd(Vec, Vec);
Vec vecsub(Vec, Vec);
Vec vecmul(Vec, double);
Vec vecnorm(Vec);
double vecdist(Vec, Vec);
double vecdot(Vec, Vec);
Vec vecnormal(Vec);
int objcoll(Obj *, Obj *);
void freeobj(Obj *);
void objexcise(Obj *);
void addtray(ObjT *, ...);
void physstep(void);
int hinged(Obj *, Obj *);
void copyhinges(Obj *, Obj *);

View File

@ -0,0 +1,14 @@
</$objtype/mkfile
BIN=/$objtype/bin/games
TARG=timmy
OFILES=\
timmy.$O\
simple.$O\
poly.$O\
util.$O\
phys.$O\
HFILES=dat.h fns.h
</sys/src/cmd/mkone

219
sys/src/games/timmy/phys.c Normal file
View File

@ -0,0 +1,219 @@
#include <u.h>
#include <libc.h>
#include <draw.h>
#include "dat.h"
#include "fns.h"
extern Obj runo;
#define Grav 10
#define Dt 0.01
#define Beta 0.5
int Steps = 4;
typedef struct Contact Contact;
struct Contact {
Obj *vo, *eo;
Vec v, n;
double pen;
double impn, impt;
double targun;
};
enum { CTSBLOCK = 64 };
Contact *cts;
int icts, ncts;
static void
colldect(Obj *a, Obj *b)
{
int i, j;
Vec *x, *y, *z;
double d, m;
Poly *ap, *bp;
ap = a->poly;
bp = b->poly;
for(i = 0; i < ap->nvert; i++){
z = &ap->vert[i];
m = Inf(1);
for(j = 0; j < bp->nvert; j++){
x = &bp->vert[j];
y = x + 1;
d = -(z->x - x->x) * (y->y - x->y) + (z->y - x->y) * (y->x - x->x);
d /= vecdist(*x, *y);
if(d < -Slop) goto next;
if(d < m){
if(m < 0) goto next;
m = d;
if(icts == ncts)
cts = realloc(cts, sizeof(Contact) * (ncts += CTSBLOCK));
cts[icts] = (Contact){a, b, *z, vecnormal(vecsub(*x, *y)), d, 0, 0, 0};
}else if(d < 0) goto next;
}
icts++;
next: ;
}
}
static void
collresp(Contact *p)
{
double μs, μd;
Vec n, t, u, r0, r1, Δp;
double ut, un, α, γ, γt, γn, pt, mt, mn;
double r0n, r0t, r1n, r1t;
double mv, me, Iv, Ie;
n = p->n;
t = (Vec){n.y, -n.x};
mv = p->vo->tab->imass;
me = p->eo->tab->imass;
Iv = mv * p->vo->poly->invI;
Ie = me * p->eo->poly->invI;
r0 = vecsub(p->v, p->vo->p);
r1 = vecsub(p->v, p->eo->p);
Δp.x = -(t.x * p->impt + n.x * p->impn);
Δp.y = -(t.y * p->impt + n.y * p->impn);
p->vo->v = vecadd(p->vo->v, vecmul(Δp, mv));
p->vo->ω -= (Δp.x * r0.y - Δp.y * r0.x) * Iv;
p->eo->v = vecadd(p->eo->v, vecmul(Δp, -me));
p->eo->ω += (Δp.x * r1.y - Δp.y * r1.x) * Ie;
u.x = p->vo->v.x - p->vo->ω * r0.y - p->eo->v.x + p->eo->ω * r1.y;
u.y = p->vo->v.y + p->vo->ω * r0.x - p->eo->v.y - p->eo->ω * r1.x;
ut = vecdot(u, t);
un = vecdot(u, n);
r0t = vecdot(r0, t);
r0n = vecdot(r0, n);
r1t = vecdot(r1, t);
r1n = vecdot(r1, n);
γ = 0; /* accumulated normal impulse */
pt = 0; /* accumulated transverse impulse */
μs = 0.5;
μd = 0.3;
un += p->targun;
if(un >= 0 && p->pen <= 0){
p->impt = 0;
p->impn = 0;
return;
}
if(p->pen > 0){
un -= Beta * p->pen / Dt;
if(un >= 0){
mn = mv + r0t * r0t * Iv;
mn += me + r1t * r1t * Ie;
γ = -un/mn;
un = 0;
}
}
while(un < 0){
/* calculate α, the effective coefficient of friction */
if(ut == 0){
α = r0t * r0n * Iv + r1t * r1n * Ie;
α /= mv + r0n * r0n * Iv + me + r1n * r1n * Ie;
if(α > μs) α = μd;
else if(α < -μs) α = -μd;
}else
α = ut < 0 ? μd : -μd;
mt = α * mv + (r0n * r0n * α - r0t * r0n) * Iv;
mt += α * me + (r1n * r1n * α - r1t * r1n) * Ie;
mn = mv + (r0t * r0t - r0n * r0t * α) * Iv;
mn += me + (r1t * r1t - r1n * r1t * α) * Ie;
/* determine events which would change α */
if(ut == 0) γt = Inf(1);
else{
γt = γ - ut / mt;
if(γt < γ) γt = Inf(1);
}
γn = γ - un / mn;
if(γn < γ) γn = Inf(1);
/* choose earlier one */
if(γt < γn){
ut = 0;
un += mn * (γt - γ);
pt += (γt - γ) * α;
γ = γt;
}else{
assert(γn < Inf(1));
un = 0;
ut += mt * (γn - γ);
pt += (γn - γ) * α;
γ = γn;
}
}
p->impt = pt;
p->impn = γ;
Δp.x = t.x * pt + n.x * γ;
Δp.y = t.y * pt + n.y * γ;
p->vo->v = vecadd(p->vo->v, vecmul(Δp, mv));
p->vo->ω -= (Δp.x * r0.y - Δp.y * r0.x) * Iv;
p->eo->v = vecadd(p->eo->v, vecmul(Δp, -me));
p->eo->ω += (Δp.x * r1.y - Δp.y * r1.x) * Ie;
}
extern Hinge *hinges;
static void
hingeresp(Hinge *h)
{
Obj *a, *b;
Vec u, Δp, r0, r1;
double ma, mb, Ia, Ib;
double mxx, mxy, myy, det;
a = h->o;
b = h->cnext->o;
ma = a->tab->imass;
mb = b->tab->imass;
Ia = ma * a->poly->invI;
Ib = mb * b->poly->invI;
r0 = vecsub(h->p, a->p);
r1 = vecsub(h->cnext->p, b->p);
u.x = a->v.x - a->ω * r0.y - b->v.x + b->ω * r1.y;
u.y = a->v.y + a->ω * r0.x - b->v.y - b->ω * r1.x;
u.x += Beta * (h->p.x - h->cnext->p.x) / Dt;
u.y += Beta * (h->p.y - h->cnext->p.y) / Dt;
mxx = ma + Ia * r0.x * r0.x + mb + Ib * r1.x * r1.x;
mxy = Ia * r0.x * r0.y + Ib * r1.x * r1.y;
myy = ma + Ia * r0.y * r0.y + mb + Ib * r1.y * r1.y;
det = mxx * myy - mxy * mxy;
Δp.x = (mxx * u.x + mxy * u.y) / det;
Δp.y = (myy * u.y + mxy * u.x) / det;
a->v = vecadd(a->v, vecmul(Δp, -ma));
a->ω += (Δp.x * r0.y - Δp.y * r0.x) * Ia;
b->v = vecadd(b->v, vecmul(Δp, mb));
b->ω -= (Δp.x * r1.y - Δp.y * r1.x) * Ib;
u.x = a->v.x - a->ω * r0.y - b->v.x + b->ω * r1.y;
u.y = a->v.y + a->ω * r0.x - b->v.y - b->ω * r1.x;
}
void
physstep(void)
{
Obj *o, *a, *b;
int i, j, k;
Hinge *p;
for(k = 0; k < Steps; k++){
for(o = runo.next; o != &runo; o = o->next)
if(o->tab->imass != 0)
o->v.y += Grav * Dt;
icts = 0;
for(a = runo.next; a != &runo; a = a->next)
for(b = a->next; b != &runo; b = b->next){
if(!rectXrect(a->bbox, b->bbox) || a->poly == nil || b->poly == nil || hinged(a, b)) continue;
colldect(a, b);
colldect(b, a);
}
for(j = 0; j < 10; j++){
for(i = 0; i < icts; i++)
collresp(&cts[i]);
for(p = hinges; p != nil; p = p->anext)
hingeresp(p);
}
for(o = runo.next; o != &runo; o = o->next)
o->tab->move(o, o->p.x + o->v.x * Dt, o->p.y + o->v.y * Dt, o->θ + o->ω * Dt / DEG);
}
}

323
sys/src/games/timmy/poly.c Normal file
View File

@ -0,0 +1,323 @@
#include <u.h>
#include <libc.h>
#include <thread.h>
#include <draw.h>
#include <mouse.h>
#include "dat.h"
#include "fns.h"
Poly *
mkpoly(int n, ...)
{
Poly *p;
int i;
va_list va;
p = emalloc(sizeof(Poly));
p->nvert = n;
p->vert = emalloc((n + 1) * sizeof(Vec));
va_start(va, n);
for(i = 0; i < n; i++){
p->vert[i].x = va_arg(va, double);
p->vert[i].y = va_arg(va, double);
}
p->vert[n] = p->vert[0];
va_end(va);
polyfix(p);
return p;
}
void
polyfix(Poly *o)
{
double I, A, x, y, t;
Vec *p, *q;
int i;
I = 0;
A = 0;
x = 0;
y = 0;
for(i = 0; i < o->nvert; i++){
p = &o->vert[i];
q = p + 1;
t = p->x * q->y - p->y * q->x;
A += t;
x += (p->x + q->x) * t / 3;
y += (p->y + q->y) * t / 3;
}
x /= A;
y /= A;
for(i = 0; i <= o->nvert; i++){
o->vert[i].x -= x;
o->vert[i].y -= y;
}
for(i = 0; i < o->nvert; i++){
p = &o->vert[i];
q = p + 1;
t = p->x * q->y - p->y * q->x;
I += (p->x * (p->x + q->x) + q->x * q->x + p->y * (p->y + q->y) + q->y * q->y) * t / 6;
}
o->invI = A / I;
}
Poly *
polydup(Poly *p)
{
Poly *q;
int i;
q = emalloc(sizeof(Poly));
q->nvert = p->nvert;
q->vert = emalloc((p->nvert + 1) * sizeof(Vec));
for(i = 0; i <= p->nvert; i++)
q->vert[i] = p->vert[i];
q->invI = p->invI;
return q;
}
void
polytrans(Poly *sp, Poly *dp, double x0, double y0, double θ)
{
int i;
double c, s, x, y;
assert(sp->nvert == dp->nvert);
c = cos(θ * DEG);
s = sin(θ * DEG);
for(i = 0; i <= sp->nvert; i++){
x = sp->vert[i].x;
y = sp->vert[i].y;
dp->vert[i].x = x0 + x * c - y * s;
dp->vert[i].y = y0 + x * s + y * c;
}
dp->invI = sp->invI;
}
void
polybbox(Poly *sp, Rectangle *t)
{
int fx, fy, cx, cy, i;
t->min.x = floor(sp->vert[0].x - Slop);
t->max.x = ceil(sp->vert[0].x + Slop) + 1;
t->min.y = floor(sp->vert[0].y - Slop);
t->max.y = ceil(sp->vert[0].y + Slop) + 1;
for(i = 1; i < sp->nvert; i++){
fx = sp->vert[i].x;
cx = ceil(fx + Slop); fx = floor(fx - Slop);
fy = sp->vert[i].y + 1;
cy = ceil(fy + Slop); fy = floor(fy - Slop);
if(fx < t->min.x) t->min.x = fx;
if(cx > t->max.x) t->max.x = cx;
if(fy < t->min.y) t->min.y = fy;
if(cy > t->max.y) t->max.y = cy;
}
}
void
polydraw(Poly *p, Image *d, Image *fill, Image *line)
{
int i;
static int maxp;
static Point *buf;
if(p->nvert + 1 > maxp){
maxp = p->nvert + 1;
free(buf);
buf = emalloc((p->nvert + 1) * sizeof(Point));
}
for(i = 0; i <= p->nvert; i++){
buf[i].x = d->r.min.x + (int)(p->vert[i].x + 0.5);
buf[i].y = d->r.min.y + (int)(p->vert[i].y + 0.5);
}
if(fill != nil) fillpoly(d, buf, p->nvert + 1, 0, fill, ZP);
if(line != nil) poly(d, buf, p->nvert + 1, 0, 0, 0, line, ZP);
}
void
freepoly(Poly *p)
{
if(p == nil) return;
free(p->vert);
free(p);
}
Hinge *
hingedup(Hinge *h, Obj *o)
{
Hinge *p, **hp, *r;
r = nil;
hp = &r;
for(; h != nil; h = h->onext){
p = emalloc(sizeof(Hinge));
p->p = h->p;
p->p0 = h->p0;
p->o = o;
p->cnext = p->cprev = p;
*hp = p;
hp = &p->onext;
}
return r;
}
Obj *
mkobj(ObjT *t)
{
Obj *o;
o = emalloc(sizeof(Obj));
o->tab = t;
o->hinge = hingedup(t->hinge, o);
o->tab->init(o);
o->next = o->prev = o;
return o;
}
Obj *
objdup(Obj *o)
{
Obj *p;
p = emalloc(sizeof(Obj));
*p = *o;
p->poly = polydup(o->poly);
p->next = p->prev = p;
p->hinge = hingedup(p->hinge, p);
return p;
}
void
objcat(Obj *l, Obj *o)
{
o->prev = l->prev;
o->next = l;
o->prev->next = o;
o->next->prev = o;
}
static int
polycheck(Poly *a, Poly *b)
{
int i, j;
Vec *x, *y, *z;
double d, m;
for(i = 0; i < a->nvert; i++){
z = &a->vert[i];
m = Inf(1);
for(j = 0; j < b->nvert; j++){
x = &b->vert[j];
y = x + 1;
d = (z->y - x->y) * (y->x - x->x) - (z->x - x->x) * (y->y - x->y);
d /= vecdist(*x, *y);
if(d < -Slop) goto next;
if(d < m){
if(m < 0) goto next;
m = d;
}else if(d < 0) goto next;
}
return 1;
next:;
}
return 0;
}
int
objcoll(Obj *a, Obj *b)
{
if(!rectXrect(a->bbox, b->bbox)) return 0;
if(a->poly == nil || b->poly == nil) return 0;
return polycheck(a->poly, b->poly) || polycheck(b->poly, a->poly);
}
void
objexcise(Obj *o)
{
Hinge *h;
o->next->prev = o->prev;
o->prev->next = o->next;
o->next = o;
o->prev = o;
for(h = o->hinge; h != nil; h = h->onext){
h->cprev->cnext = h->cnext;
h->cnext->cprev = h->cprev;
h->cprev = h;
h->cnext = h;
}
}
void
freeobj(Obj *o)
{
if(o == nil) return;
objexcise(o);
freepoly(o->poly);
free(o);
}
int
hinged(Obj *a, Obj *b)
{
Hinge *k, *l, *m;
if(b->hinge == nil) return 0;
for(k = a->hinge; k != nil; k = k->onext)
for(l = k->cnext; l != k; l = l->cnext)
for(m = b->hinge; m != nil; m = m->onext)
if(m == l)
return 1;
return 0;
}
Hinge *hinges;
void
hingepairs(Obj *l)
{
Obj *o;
Hinge *h, *hh;
Hinge **p;
hinges = nil;
p = &hinges;
for(o = l->next; o != l; o = o->next)
for(h = o->hinge; h != nil; h = h->onext){
for(hh = h->cnext; hh != h; hh = hh->cnext)
if(hh < h)
break;
if(hh == h) continue;
*p = h;
p = &h->anext;
}
}
void
copyhinges(Obj *sl, Obj *dl)
{
Obj *o, *p, **ol;
Hinge *h, *k, *l;
int n;
for(n = 0, o = sl->next; o != sl; o = o->next)
o->idx = n++;
ol = emalloc(sizeof(Obj *) * n);
for(n = 0, o = dl->next; o != dl; o = o->next)
ol[n++] = o;
for(o = sl->next, p = dl->next; o != sl; o = o->next, p = p->next){
for(h = o->hinge, k = p->hinge; h != nil; h = h->onext, k = k->onext){
if(h->cnext == h) continue;
for(l = h->cnext->o->hinge, n = 0; l != h->cnext; l = l->onext)
n++;
for(l = ol[h->cnext->o->idx]->hinge; n != 0; n--)
l = l->onext;
l->cprev->cnext = k->cnext;
k->cnext->cprev = l->cprev;
k->cnext = l;
l->cprev = k;
}
}
hingepairs(dl);
}

View File

@ -0,0 +1,107 @@
#include <u.h>
#include <libc.h>
#include <draw.h>
#include "dat.h"
#include "fns.h"
void
objpolyinit(Obj *o)
{
o->poly = polydup(o->tab->poly);
}
void
objpolymove(Obj *o, double x, double y, double θ)
{
Hinge *h;
double c, s;
o->p.x = x;
o->p.y = y;
o->θ = θ;
polytrans(o->tab->poly, o->poly, x, y, θ);
polybbox(o->poly, &o->bbox);
if(o->hinge != nil){
c = cos(θ * DEG);
s = sin(θ * DEG);
for(h = o->hinge; h != nil; h = h->onext){
h->p.x = c * h->p0.x - s * h->p0.y + x;
h->p.y = s * h->p0.x + c * h->p0.y + y;
}
}
}
void
objpolydraw(Obj *o, Image *i)
{
Hinge *h;
Point p;
polydraw(o->poly, i, o->tab->fill, o->tab->line);
for(h = o->hinge; h != nil; h = h->onext){
p.x = i->r.min.x + (int)(h->p.x + 0.5);
p.y = i->r.min.y + (int)(h->p.y + 0.5);
ellipse(i, p, 2, 2, 0, display->black, ZP);
}
}
void
mkobjpoly(ObjT *t, Poly *p, Image *fill, Image *line, double imass)
{
t->init = objpolyinit;
t->draw = objpolydraw;
t->move = objpolymove;
t->poly = p;
t->line = line;
t->fill = fill;
t->imass = imass;
}
void
addhinge(ObjT *t, double x, double y)
{
Hinge *h, **hp;
h = emalloc(sizeof(Hinge));
h->p.x = x;
h->p.y = y;
h->p0 = h->p;
h->cnext = h->cprev = h;
for(hp = &t->hinge; *hp != nil; hp = &(*hp)->onext)
;
*hp = h;
}
Poly *
mkball(int n)
{
Poly *p;
int i;
p = emalloc(sizeof(Poly));
p->nvert = n;
p->vert = emalloc(sizeof(Vec) * (n + 1));
for(i = 0; i < n; i++){
p->vert[i].x = 10 * cos(2 * PI * i / n);
p->vert[i].y = 10 * sin(2 * PI * i / n);
}
p->vert[n] = p->vert[0];
polyfix(p);
return p;
}
ObjT tdomino, tboard, thboard, tball, tfix;
void
simpleinit(void)
{
mkobjpoly(&tdomino, mkpoly(4, 0.0, 0.0, 10.0, 0.0, 10.0, 40.0, 0.0, 40.0), rgb(0xFF0000FF), display->black, 10);
mkobjpoly(&tboard, mkpoly(4, 0.0, 0.0, 100.0, 0.0, 100.0, 6.0, 0.0, 6.0), rgb(0x663300FF), nil, 0);
mkobjpoly(&thboard, mkpoly(4, 0.0, 0.0, 100.0, 0.0, 100.0, 6.0, 0.0, 6.0), rgb(0x884400FF), nil, 0.5);
addhinge(&thboard, 48.0, 0.0);
addhinge(&thboard, -48.0, 0.0);
mkobjpoly(&tball, mkball(17), rgb(0x00FF00FF), nil, 3);
mkobjpoly(&tfix, mkpoly(3, 0.0, 0.0, 10.0, -17.3, 20.0, 0.0), rgb(0x663300FF), display->black, 0);
addhinge(&tfix, 0.0, 0.0);
addtray(&tdomino, &tboard, &thboard, &tfix, &tball, nil);
}

328
sys/src/games/timmy/timmy.c Normal file
View File

@ -0,0 +1,328 @@
#include <u.h>
#include <libc.h>
#include <thread.h>
#include <draw.h>
#include <mouse.h>
#include <keyboard.h>
#include <cursor.h>
#include "dat.h"
#include "fns.h"
Screen *scr;
Image *work, *tray;
Image *grey;
Obj trayo;
Obj worko;
Obj runo;
Mousectl *mc;
Keyboardctl *kc;
Obj *carry;
int showcarry;
extern int Steps;
void *
emalloc(ulong sz)
{
void *v;
v = malloc(sz);
if(v == nil) sysfatal("malloc: %r");
memset(v, 0, sz);
setmalloctag(v, getcallerpc(&sz));
return v;
}
Image *
rgb(u32int c)
{
return allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, c);
}
void
addtray(ObjT *t, ...)
{
Obj *o;
va_list va;
static double trayw;
va_start(va, t);
for(; t != nil; t = va_arg(va, ObjT *)){
o = mkobj(t);
o->tab->move(o, 0, 0, 0);
trayw += TraySpc;
o->tab->move(o, trayw + Dx(o->bbox)/2, TrayH/2, 0);
trayw += Dx(o->bbox);
objcat(&trayo, o);
}
va_end(va);
}
static void
drawtray(void)
{
Obj *o;
for(o = trayo.next; o != &trayo; o = o->next)
o->tab->draw(o, tray);
}
static void
screeninit(void)
{
grey = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0xCCCCCCFF);
scr = allocscreen(screen, display->white, 0);
work = allocwindow(scr, Rect(screen->r.min.x, screen->r.min.y, screen->r.max.x, screen->r.max.y - TrayH), 0, 0xFFFFFFFF);
tray = allocwindow(scr, Rect(screen->r.min.x, screen->r.max.y - TrayH, screen->r.max.x, screen->r.max.y), 0, 0xCCCCCCFF);
}
static Obj *
objclick(Point p, Obj *l)
{
Obj *o;
for(o = l->next; o != l; o = o->next)
if(ptinrect(p, o->bbox))
return o;
return nil;
}
static void
workdraw(void)
{
Obj *o;
draw(work, work->r, display->white, nil, ZP);
for(o = worko.next; o != &worko; o = o->next)
o->tab->draw(o, work);
if(carry != nil && showcarry)
carry->tab->draw(carry, work);
flushimage(display, 1);
}
static void
rundraw(void)
{
Obj *o;
draw(work, work->r, display->white, nil, ZP);
for(o = runo.next; o != &runo; o = o->next)
o->tab->draw(o, work);
flushimage(display, 1);
}
static int
canhinge(Obj *a, Obj *b)
{
Hinge *h, *k;
if(a->hinge == nil || b->hinge == nil) return 0;
for(h = a->hinge; h != nil; h = h->onext)
for(k = b->hinge; k != nil; k = k->onext)
if(vecdist(h->p, k->p) <= HingeSep)
return 1;
return 0;
}
static int
hinge(Obj *a, Obj *b)
{
Hinge *h, *k, *l;
if(a->hinge == nil || b->hinge == nil) return 0;
for(h = a->hinge; h != nil; h = h->onext)
for(k = b->hinge; k != nil; k = k->onext)
if(vecdist(h->p, k->p) <= HingeSep){
h->cprev->cnext = k;
k->cprev->cnext = h;
l = h->cprev;
h->cprev = k->cprev;
k->cprev = l;
b->tab->move(b, b->p.x + h->p.x - k->p.x, b->p.y + h->p.y - k->p.y, b->θ);
return 1;
}
return 0;
}
static void
place(void)
{
Obj *o;
int hinges;
hinges = 0;
for(o = worko.next; o != &worko; o = o->next)
if(objcoll(o, carry))
if(canhinge(o, carry))
hinges++;
else
return;
for(o = worko.next; hinges > 0 && o != &worko; o = o->next)
if(objcoll(o, carry))
hinges -= hinge(o, carry);
if(hinges != 0) print("hinge error\n");
objcat(&worko, carry);
carry = nil;
workdraw();
}
static void
mouse(void)
{
static int lbut = -1;
Point p;
if(lbut < 0)
lbut = mc->buttons;
if(ptinrect(mc->xy, work->r)){
p = subpt(mc->xy, work->r.min);
if(carry != nil && (carry->p.x != p.x || carry->p.y != p.y || !showcarry)){
carry->tab->move(carry, p.x, p.y, carry->θ);
showcarry = 1;
workdraw();
}
}else if(showcarry){
showcarry = 0;
if(carry != nil)
workdraw();
}
if((~mc->buttons & lbut & 1) != 0){
if(ptinrect(mc->xy, tray->r)){
carry = objclick(subpt(mc->xy, tray->r.min), &trayo);
if(carry != nil)
carry = objdup(carry);
}else if(ptinrect(mc->xy, work->r)){
if(carry != nil)
place();
else{
carry = objclick(subpt(mc->xy, work->r.min), &worko);
if(carry != nil)
objexcise(carry);
}
}
}
if((~mc->buttons & lbut & 4) != 0){
if(carry != nil){
freeobj(carry);
carry = nil;
showcarry = 0;
workdraw();
}else if(ptinrect(mc->xy, work->r)){
carry = objclick(subpt(mc->xy, work->r.min), &worko);
if(carry != nil)
carry = objdup(carry);
}
}
lbut = mc->buttons;
}
static void
run(void)
{
Obj *o, *oo;
Rune r;
static Cursor cursor;
for(o = runo.next; o != &runo; o = oo){
oo = o->next;
freeobj(o);
}
for(o = worko.next; o != &worko; o = o->next)
objcat(&runo, objdup(o));
copyhinges(&worko, &runo);
setcursor(mc, &cursor);
for(;;){
Alt a[] = {
{mc->c, &mc->Mouse, CHANRCV},
{kc->c, &r, CHANRCV},
{nil, nil, CHANNOBLK}
};
switch(alt(a)){
case 0: mouse(); break;
case 1:
switch(r){
case ' ': goto out;
case Kdel: threadexitsall(nil);
}
}
physstep();
rundraw();
}
out:
workdraw();
setcursor(mc, nil);
}
static void
key(Rune r)
{
switch(r){
case Kdel:
threadexitsall(nil);
case 'w':
if(carry != nil){
carry->tab->move(carry, carry->p.x, carry->p.y, carry->θ - 15);
workdraw();
}
break;
case 'e':
if(carry != nil){
carry->tab->move(carry, carry->p.x, carry->p.y, carry->θ + 15);
workdraw();
}
break;
case ' ':
run();
break;
}
}
static void
usage(void)
{
fprint(2, "usage: %s [-s steps]\n", argv0);
threadexitsall("usage");
}
void
threadmain(int argc, char **argv)
{
void simpleinit(void);
Rune r;
char *s;
ARGBEGIN{
case 's':
Steps = strtol(EARGF(usage()), &s, 0);
if(*s != 0) usage();
break;
default: usage();
}ARGEND;
if(initdraw(nil, nil, nil) < 0) sysfatal("initdraw: %r");
mc = initmouse(nil, screen);
if(mc == nil) sysfatal("initmouse: %r");
kc = initkeyboard(nil);
if(kc == nil) sysfatal("initkeyboard: %r");
screeninit();
trayo.prev = trayo.next = &trayo;
worko.prev = worko.next = &worko;
runo.prev = runo.next = &runo;
simpleinit();
drawtray();
flushimage(display, 1);
for(;;){
Alt a[] = {
{mc->c, &mc->Mouse, CHANRCV},
{kc->c, &r, CHANRCV},
{nil, nil, CHANEND}
};
switch(alt(a)){
case 0: mouse(); break;
case 1: key(r); break;
}
}
}

View File

@ -0,0 +1,60 @@
#include <u.h>
#include <libc.h>
#include <thread.h>
#include <draw.h>
#include <mouse.h>
#include <keyboard.h>
#include "dat.h"
#include "fns.h"
Vec
vecadd(Vec a, Vec b)
{
return (Vec){a.x + b.x, a.y + b.y};
}
Vec
vecsub(Vec a, Vec b)
{
return (Vec){a.x - b.x, a.y - b.y};
}
Vec
vecmul(Vec v, double s)
{
return (Vec){v.x * s, v.y * s};
}
Vec
vecnorm(Vec v)
{
double r;
r = hypot(v.x, v.y);
if(r == 0) return (Vec){0, 0};
v.x /= r;
v.y /= r;
return v;
}
double
vecdist(Vec a, Vec b)
{
return hypot(a.x - b.x, a.y - b.y);
}
double
vecdot(Vec a, Vec b)
{
return a.x * b.x + a.y * b.y;
}
Vec
vecnormal(Vec n)
{
Vec m;
m.x = -n.y;
m.y = n.x;
return vecnorm(m);
}