added games/timmy
parent
baf20a548b
commit
4821c261c4
|
@ -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).
|
|
@ -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
|
|
@ -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 *);
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
Loading…
Reference in New Issue