shithub: etoys

ref: a829fc8b5a2ab396fe46ffbe8c01dcdb9d3038f6
dir: /rframeviz.c/

View raw version
#include <u.h>
#include <libc.h>
#include <thread.h>
#include <draw.h>
#include <mouse.h>
#include <keyboard.h>
#include <geometry.h>

enum {
	PCBg,
	PCFg,
	PCAux,
	NCOLORS
};

typedef struct Rectangle2 Rectangle2;
typedef struct Container Container;

/* min = bottom-left, max = top-right */
struct Rectangle2
{
	Point2 min, max;
};

struct Container
{
	RFrame;
	Rectangle2 bbox;	/* defined wrt worldrf */
};

RFrame worldrf;
Image *pal[NCOLORS];
Container containers[4];
Point2 thepoint;

void resized(void);

Rectangle2
Rpt2(Point2 min, Point2 max)
{
	return (Rectangle2){min, max};
}

Rectangle2
Rect2(double minx, double miny, double maxx, double maxy)
{
	/* assumed that w is always 1 */
	return Rpt2(Pt2(minx,miny,1), Pt2(maxx,maxy,1));
}

Rectangle2
rectaddpt2(Rectangle2 r, Point2 p)
{
	return Rpt2(addpt2(r.min, p), addpt2(r.max, p));
}

int
ptinrect2(Point2 p, Rectangle2 r)
{
	return p.x >= r.min.x && p.x < r.max.x &&
		p.y >= r.min.y && p.y < r.max.y;
}

Point2
perppt2(Point2 p)
{
	return Pt2(-p.y,p.x,p.w);
}

void
initcontainers(Rectangle parent)
{
	int w, h, i;

	w = Dx(parent);
	h = Dy(parent);

	containers[0].bbox = Rpt2(Pt2(0,0,1), Pt2(w/2,h/2,1));
	containers[1].bbox = rectaddpt2(containers[0].bbox, Vec2(w/2,0));
	containers[2].bbox = rectaddpt2(containers[0].bbox, Vec2(0,h/2));
	containers[3].bbox = rectaddpt2(containers[0].bbox, Vec2(w/2,h/2));

	for(i = 0; i < nelem(containers); i++)
		containers[i].p = addpt2(containers[i].bbox.min, Vec2(w/4,h/4));

	containers[0].bx = Vec2(0,  1);
	containers[1].bx = Vec2(2,  0);
	containers[2].bx = Vec2(0.5,0);
	containers[3].bx = Vec2(1,  0);

	containers[0].by = Vec2(1,0);
	containers[1].by = Vec2(0,2);
	containers[2].by = Vec2(0,1);
	containers[3].by = Vec2(0,1);
}

Point
toscreen(Point2 p)
{
	p = invrframexform(p, worldrf);
	return Pt(p.x,p.y);
}

Point2
fromscreen(Point p)
{
	return rframexform(Pt2(p.x,p.y,1), worldrf);
}

void
initpalette(void)
{
	pal[PCBg] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DBlack);
	pal[PCFg] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DWhite);
	pal[PCAux] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DRed);
}

void
drawaxes(Container *c)
{
	Point2 painter;

	/* draw positive x axis */
	painter = c->p;
	while(ptinrect2(painter, c->bbox)){
		line(screen, toscreen(painter), toscreen(addpt2(painter, mulpt2(c->bx, 10))), Endsquare, Endsquare, 0, pal[PCFg], ZP);
		painter = addpt2(painter, mulpt2(c->bx, 10));
		/* unit markers */
		line(screen, toscreen(painter), toscreen(addpt2(painter, mulpt2(normvec2(perppt2(c->bx)), 4))), Endsquare, Endsquare, 0, pal[PCFg], ZP);
		line(screen, toscreen(painter), toscreen(subpt2(painter, mulpt2(normvec2(perppt2(c->bx)), 4))), Endsquare, Endsquare, 0, pal[PCFg], ZP);
	}
	/* draw negative x axis */
	painter = c->p;
	while(ptinrect2(painter, c->bbox)){
		line(screen, toscreen(painter), toscreen(subpt2(painter, mulpt2(c->bx, 10))), Endsquare, Endsquare, 0, pal[PCFg], ZP);
		painter = subpt2(painter, mulpt2(c->bx, 10));
		/* unit markers */
		line(screen, toscreen(painter), toscreen(addpt2(painter, mulpt2(normvec2(perppt2(c->bx)), 4))), Endsquare, Endsquare, 0, pal[PCFg], ZP);
		line(screen, toscreen(painter), toscreen(subpt2(painter, mulpt2(normvec2(perppt2(c->bx)), 4))), Endsquare, Endsquare, 0, pal[PCFg], ZP);
	}

	/* draw positive y axis */
	painter = c->p;
	while(ptinrect2(painter, c->bbox)){
		line(screen, toscreen(painter), toscreen(addpt2(painter, mulpt2(c->by, 10))), Endsquare, Endsquare, 0, pal[PCFg], ZP);
		painter = addpt2(painter, mulpt2(c->by, 10));
		/* unit markers */
		line(screen, toscreen(painter), toscreen(addpt2(painter, mulpt2(normvec2(perppt2(c->by)), 4))), Endsquare, Endsquare, 0, pal[PCFg], ZP);
		line(screen, toscreen(painter), toscreen(subpt2(painter, mulpt2(normvec2(perppt2(c->by)), 4))), Endsquare, Endsquare, 0, pal[PCFg], ZP);
	}
	/* draw negative y axis */
	painter = c->p;
	while(ptinrect2(painter, c->bbox)){
		line(screen, toscreen(painter), toscreen(subpt2(painter, mulpt2(c->by, 10))), Endsquare, Endsquare, 0, pal[PCFg], ZP);
		painter = subpt2(painter, mulpt2(c->by, 10));
		/* unit markers */
		line(screen, toscreen(painter), toscreen(addpt2(painter, mulpt2(normvec2(perppt2(c->by)), 4))), Endsquare, Endsquare, 0, pal[PCFg], ZP);
		line(screen, toscreen(painter), toscreen(subpt2(painter, mulpt2(normvec2(perppt2(c->by)), 4))), Endsquare, Endsquare, 0, pal[PCFg], ZP);
	}
}

void
drawcontainers(void)
{
	char buf[128];
	int i;

	line(screen, addpt(screen->r.min, Pt(Dx(screen->r)/2,0)), subpt(screen->r.max, Pt(Dx(screen->r)/2, 0)), Endsquare, Endsquare, 0, pal[PCFg], ZP);
	line(screen, addpt(screen->r.min, Pt(0,Dy(screen->r)/2)), subpt(screen->r.max, Pt(0,Dy(screen->r)/2)), Endsquare, Endsquare, 0, pal[PCFg], ZP);

	for(i = 0; i < nelem(containers); i++){
		drawaxes(&containers[i]);

		if(ptinrect2(invrframexform(thepoint, containers[i]), containers[i].bbox))
			fillellipse(screen, toscreen(invrframexform(thepoint, containers[i])), 2, 2, pal[PCAux], ZP);

		snprint(buf, sizeof buf, "bx %v", containers[i].bx);
		stringn(screen, toscreen(addpt2(containers[i].bbox.min, Vec2(10,2.5*font->height))), pal[PCFg], ZP, font, buf, sizeof buf);

		snprint(buf, sizeof buf, "by %v", containers[i].by);
		stringn(screen, toscreen(addpt2(containers[i].bbox.min, Vec2(10,1.5*font->height))), pal[PCFg], ZP, font, buf, sizeof buf);
	}
}

void
redraw(void)
{
	lockdisplay(display);
	draw(screen, screen->r, pal[PCBg], nil, ZP);
	drawcontainers();
	flushimage(display, 1);
	unlockdisplay(display);
}

void
rmb(Mousectl *mc, Keyboardctl *kc)
{
	Point2 mpos, nbx, nby;
	char buf[128], *s;
	int i;

	mpos = fromscreen(mc->xy);
	for(i = 0; i < nelem(containers); i++)
		if(ptinrect2(mpos, containers[i].bbox)){
			nbx = containers[i].bx;
			snprint(buf, sizeof buf, "%g %g", nbx.x, nbx.y);
			if(enter("bx", buf, sizeof buf, mc, kc, nil) <= 0)
				return;
			nbx.x = strtod(buf, &s);
			if(*s != ' ')
				return;
			nbx.y = strtod(s, &s);
			if(*s != 0)
				return;

			nby = containers[i].by;
			snprint(buf, sizeof buf, "%g %g", nby.x, nby.y);
			if(enter("by", buf, sizeof buf, mc, kc, nil) <= 0)
				return;
			nby.x = strtod(buf, &s);
			if(*s != ' ')
				return;
			nby.y = strtod(s, &s);
			if(*s != 0)
				return;

			containers[i].bx = nbx;
			containers[i].by = nby;
		}
}

void
lmb(Mousectl *mc, Keyboardctl *)
{
	Point2 mpos;
	int i;

	mpos = fromscreen(mc->xy);
	for(i = 0; i < nelem(containers); i++)
		if(ptinrect2(mpos, containers[i].bbox))
			thepoint = rframexform(mpos, containers[i]);
}

void
mouse(Mousectl *mc, Keyboardctl *kc)
{
	if((mc->buttons&1) != 0)
		lmb(mc, kc);
	if((mc->buttons&4) != 0)
		rmb(mc, kc);
}

void
key(Rune r)
{
	switch(r){
	case Kdel:
	case 'q':
		threadexitsall(nil);
	}
}

void
usage(void)
{
	fprint(2, "usage: %s\n", argv0);
	exits("usage");
}

void
threadmain(int argc, char *argv[])
{
	Mousectl *mc;
	Keyboardctl *kc;
	Rune r;

	GEOMfmtinstall();
	ARGBEGIN{
	default: usage();
	}ARGEND;
	if(argc > 0)
		usage();

	if(initdraw(nil, nil, nil) < 0)
		sysfatal("initdraw: %r");
	if((mc = initmouse(nil, screen)) == nil)
		sysfatal("initmouse: %r");
	if((kc = initkeyboard(nil)) == nil)
		sysfatal("initkeyboard: %r");
	initpalette();
	initcontainers(screen->r);
	worldrf.p = Pt2(screen->r.min.x,screen->r.max.y,1);
	worldrf.bx = Vec2(1, 0);
	worldrf.by = Vec2(0,-1);
	thepoint = Pt2(0,0,1);

	display->locking = 1;
	unlockdisplay(display);
	redraw();

	for(;;){
		enum { MOUSE, RESIZE, KEYBOARD };
		Alt a[] = {
			{mc->c, &mc->Mouse, CHANRCV},
			{mc->resizec, nil, CHANRCV},
			{kc->c, &r, CHANRCV},
			{nil, nil, CHANEND}
		};

		switch(alt(a)){
		case MOUSE:
			mouse(mc, kc);
			break;
		case RESIZE:
			resized();
			break;
		case KEYBOARD:
			key(r);
			break;
		}

		redraw();
	}
}

void
resized(void)
{
	lockdisplay(display);
	if(getwindow(display, Refnone) < 0)
		sysfatal("couldn't resize");
	unlockdisplay(display);
	worldrf.p = Pt2(screen->r.min.x,screen->r.max.y,1);
	initcontainers(screen->r);
	redraw();
}