3d color cube
diff --git a/src/cmd/draw/cmapcube.c b/src/cmd/draw/cmapcube.c
new file mode 100644
index 0000000..a7f4d92
--- /dev/null
+++ b/src/cmd/draw/cmapcube.c
@@ -0,0 +1,226 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <event.h>
+#include <geometry.h>
+
+typedef struct Vert{
+	Point3 world;
+	Point3 screen;
+	int color;
+}Vert;
+
+int		nocubes;
+int		ncolor;
+Quaternion	q;
+Image		*image;
+Image		*bg;
+Image		*color[256];
+Rectangle	viewrect;
+int		prevsel;
+
+int
+cmp(Vert *a, Vert *b)
+{
+	if(a->screen.z>b->screen.z)
+		return -1;
+	if(a->screen.z<b->screen.z)
+		return 1;
+	return 0;
+}
+
+/* crummy hack */
+void
+readcolmap(Display *d, RGB *cmap)
+{
+	int i, rgb, r, g, b;
+
+	for(i=0; i<256; i++){
+		rgb = cmap2rgb(i);
+		r = rgb>>16;
+		g = (rgb>>8)&0xFF;
+		b = rgb & 0xFF;
+		cmap[i].red = r|(r<<8)|(r<<16)|(r<<24);
+		cmap[i].green = g|(g<<8)|(g<<16)|(g<<24);
+		cmap[i].blue = b|(b<<8)|(b<<16)|(b<<24);
+	}
+}
+
+void
+colorspace(RGB *cmap, Vert *v)
+{
+	Space *view;
+	int i;
+
+	for(i=0;i!=ncolor;i++){
+		v[i].world.x=(cmap[i].red>>24)/255.-.5;
+		v[i].world.y=(cmap[i].green>>24)/255.-.5;
+		v[i].world.z=(cmap[i].blue>>24)/255.-.5;
+		v[i].world.w=1.;
+		v[i].color=i;
+	}
+	view = pushmat(0);
+	viewport(view, viewrect, 1.);
+	persp(view, 30., 3., 7.);
+	look(view, (Point3){0., 0., -5., 1.}, (Point3){0., 0., 0., 1.},
+		(Point3){0., 1., 0., 1.});
+	qrot(view, q);
+	for(i=0;i!=ncolor;i++)
+		v[i].screen = xformpointd(v[i].world, 0, view);
+	popmat(view);
+}
+
+void
+line3(Vert a, Vert b)
+{
+	line(image, Pt(a.screen.x, a.screen.y), Pt(b.screen.x, b.screen.y), 0, 0, 0, display->white, ZP);
+}
+
+
+void
+redraw(void)
+{
+	int i, m;
+	RGB cmap[256];
+	Vert v[256];
+
+	readcolmap(display, cmap);
+	colorspace(cmap, v);
+	draw(image, image->r, bg, nil, Pt(0, 0));
+	m = Dx(viewrect)/2;
+	if(m > Dy(viewrect)/2)
+		m = Dy(viewrect)/2;
+	ellipse(image, addpt(viewrect.min, divpt(Pt(Dx(viewrect), Dy(viewrect)), 2)),
+		m, m, 1, display->white, ZP);
+
+	line3(v[0], v[0x36]);
+	line3(v[0x36], v[0x32]);
+	line3(v[0x32], v[0x3F]);
+	line3(v[0x3F], v[0]);
+	
+	line3(v[0xF0], v[0xF3]);
+	line3(v[0xF3], v[0xFF]);
+	line3(v[0xFF], v[0xFC]);
+	line3(v[0xFC], v[0xF0]);
+
+	line3(v[0], v[0xF0]);
+	line3(v[0x36], v[0xF3]);
+	line3(v[0x32], v[0xFF]);
+	line3(v[0x3F], v[0xFC]);
+
+	qsort(v, ncolor, sizeof(Vert), (int(*)(const void*, const void*))cmp);
+	if(!nocubes)
+		for(i=0; i!=ncolor; i++)
+			draw(image, rectaddpt(Rect(-3, -3, 4, 4), Pt(v[i].screen.x, v[i].screen.y)),
+				color[v[i].color], nil, Pt(0, 0));
+	draw(screen, image->r, image, nil, image->r.min);
+	flushimage(display, 1);
+}
+
+void
+eresized(int new)
+{
+	int dx, dy;
+
+	if(new && getwindow(display, Refnone) < 0){
+		fprint(2, "colors: can't reattach to window: %r\n");
+		exits("reshaped");
+	}
+	draw(screen, screen->r, display->black, nil, ZP);
+	replclipr(screen, 0, insetrect(screen->r, 3));
+	viewrect = screen->clipr;
+	viewrect.min.y += stringsize(font, "0i").y + 5;
+	if(image)
+		freeimage(image);
+	image = allocimage(display, viewrect, screen->chan, 0, DNofill);
+	dx = viewrect.max.x-viewrect.min.x;
+	dy = viewrect.max.y-viewrect.min.y;
+	if(dx>dy){
+		viewrect.min.x=(viewrect.min.x+viewrect.max.x-dy)/2;
+		viewrect.max.x=viewrect.min.x+dy;
+	}
+	else{
+		viewrect.min.y=(viewrect.min.y+viewrect.max.y-dx)/2;
+		viewrect.max.y=viewrect.min.y+dx;
+	}
+	if(image==nil){
+		fprint(2, "can't allocate image\n");
+		exits("bad allocimage");
+	}
+	prevsel = -1;
+	redraw();
+}
+
+void main(int argc, char **argv){
+	Vert v[256];
+	RGB cmap[256];
+	char buf[100];
+	Point p;
+	Mouse m;
+	int i;
+	ulong bgcol;
+
+	bgcol = DNofill;
+	ARGBEGIN{
+	case 'n':
+		nocubes = 1;
+		break;
+	case 'b':
+		bgcol = DBlack;
+		break;
+	case 'w':
+		bgcol = DWhite;
+		break;
+	}ARGEND
+
+	initdraw(0,0,0);
+	ncolor=256;
+	for(i=0;i!=ncolor;i++)
+		color[i] = allocimage(display, Rect(0, 0, 1, 1), CMAP8, 1, cmap2rgba(i));
+	if(bgcol==DNofill){
+		bg = allocimage(display, Rect(0, 0, 2, 2), screen->chan, 1, DWhite);
+		draw(bg, Rect(0, 0, 1, 1), color[0], nil, Pt(0, 0));
+		draw(bg, Rect(1, 1, 2, 2), color[0], nil, Pt(0, 0));
+	}else
+		bg = allocimage(display, Rect(0,0,1,1), screen->chan, 1, bgcol);
+
+	q=(Quaternion){1.,0.,0.,0.};
+	einit(Emouse);
+	eresized(0);
+
+	for(;;){
+		m = emouse();
+		if(m.buttons&1)
+			qball(viewrect, &m, &q, redraw, 0);
+		else if(m.buttons & 2){
+			readcolmap(display, cmap);
+			colorspace(cmap, v);
+			qsort(v, ncolor, sizeof(Vert), (int(*)(const void*, const void*))cmp);
+			while(m.buttons){
+				for(i=ncolor-1; i!=0; i--){
+					if(ptinrect(m.xy, rectaddpt(Rect(-3, -3, 4, 4), Pt(v[i].screen.x, v[i].screen.y)))){
+						i = v[i].color;
+						if(i == prevsel)
+							break;
+						sprint(buf, "index %3d r %3ld g %3ld b %3ld",
+							i,
+							cmap[i].red>>24,
+							cmap[i].green>>24,
+							cmap[i].blue>>24);
+						p = addpt(screen->r.min, Pt(2,2));
+						draw(screen, Rpt(p, addpt(p, stringsize(font, buf))), display->black, nil, p);
+						string(screen, p, display->white, ZP, font, buf);
+						prevsel = i;
+						break;
+					}
+				}
+				m = emouse();
+			}
+		}else if(m.buttons&4){
+			do
+				m = emouse();
+			while(m.buttons);
+			exits(0);
+		}
+	}
+}