shithub: etoys

Download patch

ref: 596bb7a2ddf8b005602fcb0f4db741333d5f0b30
parent: 5633da27ae4132550959fc7135f7d19afa519a70
author: rodri <rgl@antares-labs.eu>
date: Thu May 16 07:06:38 EDT 2024

new toy: linetiler.

--- /dev/null
+++ b/linetiler.c
@@ -1,0 +1,343 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include <draw.h>
+#include <mouse.h>
+#include <keyboard.h>
+#include <geometry.h>
+
+#define CLAMP(n, min, max)	((n)<(min)?(min):(n)>(max)?(max):(n))
+
+enum {
+	CLIPL = 1,
+	CLIPR = 2,
+	CLIPT = 4,
+	CLIPB = 8,
+};
+
+enum {
+	PCBg0,
+	PCBg1,
+	PCFg0,
+	PCFg1,
+	PCAux,
+	NCOLORS
+};
+
+typedef struct Line Line;
+
+struct Line
+{
+	Point2 pts[2];
+};
+
+Rectangle UR = {0,0,1,1};	/* unit rectangle */
+RFrame worldrf;
+Image *pal[NCOLORS];
+Rectangle *tiles;
+int ntiles;
+Line *lines;
+int nlines;
+Line theline;
+Point2 thepoint;
+Point2 pts[2];
+int npts;
+uint paloff;
+int thickness;
+int showtheline;
+
+void resized(void);
+
+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);
+}
+
+static void
+swapi(int *a, int *b)
+{
+	int t;
+
+	t = *a;
+	*a = *b;
+	*b = t;
+}
+
+static void
+swappt(Point *a, Point *b)
+{
+	Point t;
+
+	t = *a;
+	*a = *b;
+	*b = t;
+}
+
+static int
+ptisinside(int code)
+{
+	return !code;
+}
+
+static int
+lineisinside(int code0, int code1)
+{
+	return !(code0|code1);
+}
+
+static int
+lineisoutside(int code0, int code1)
+{
+	return code0 & code1;
+}
+
+static int
+outcode(Point p, Rectangle r)
+{
+	int code;
+
+	code = 0;
+	if(p.x < r.min.x) code |= CLIPL;
+	if(p.x > r.max.x) code |= CLIPR;
+	if(p.y < r.min.y) code |= CLIPT;
+	if(p.y > r.max.y) code |= CLIPB;
+	return code;
+}
+
+/*
+ * Cohen-Sutherland rectangle-line clipping
+ */
+int
+rectclipline(Rectangle r, Point *p0, Point *p1)
+{
+	int code0, code1;
+	int Δx, Δy;
+	double m;
+
+	Δx = p1->x - p0->x;
+	Δy = p1->y - p0->y;
+	m = Δx == 0? 0: (double)Δy/Δx;
+
+	for(;;){
+		code0 = outcode(*p0, r);
+		code1 = outcode(*p1, r);
+
+		if(lineisinside(code0, code1))
+			return 0;
+		else if(lineisoutside(code0, code1))
+			return -1;
+
+		if(ptisinside(code0)){
+			swappt(p0, p1);
+			swapi(&code0, &code1);
+		}
+
+		if(code0 & CLIPL){
+			p0->y += (r.min.x - p0->x)*m;
+			p0->x = r.min.x;
+		}else if(code0 & CLIPR){
+			p0->y += (r.max.x - p0->x)*m;
+			p0->x = r.max.x;
+		}else if(code0 & CLIPT){
+			if(p0->x != p1->x && m != 0)
+				p0->x += (r.min.y - p0->y)/m;
+			p0->y = r.min.y;
+		}else if(code0 & CLIPB){
+			if(p0->x != p1->x && m != 0)
+				p0->x += (r.max.y - p0->y)/m;
+			p0->y = r.max.y;
+		}
+	}
+}
+
+void
+addline(Line l)
+{
+	lines = realloc(lines, ++nlines*sizeof(*lines));
+	lines[nlines-1] = l;
+}
+
+void
+initpalette(void)
+{
+	pal[PCBg0] = allocimage(display, UR, screen->chan, 1, DWhite);
+	pal[PCBg1] = allocimage(display, UR, screen->chan, 1, 0x333333FF);
+	pal[PCFg0] = pal[PCBg1];
+	pal[PCFg1] = pal[PCBg0];
+	pal[PCAux] = allocimage(display, UR, screen->chan, 1, DRed);
+}
+
+void
+redraw(void)
+{
+	int i;
+
+	lockdisplay(display);
+	for(i = 0; i < ntiles; i++)
+		draw(screen, rectaddpt(tiles[i], screen->r.min), pal[PCBg0+(i%2)], nil, ZP);
+	for(i = 0; i < nlines; i++)
+		line(screen, toscreen(lines[i].pts[0]), toscreen(lines[i].pts[1]), 0, 0, thickness, pal[PCFg0+(i+paloff)%2], ZP);
+	if(showtheline)
+		line(screen, toscreen(theline.pts[0]), toscreen(theline.pts[1]), 0, 0, 0, pal[PCAux], ZP);
+	fillellipse(screen, toscreen(thepoint), 2, 2, pal[PCAux], ZP);
+	flushimage(display, 1);
+	unlockdisplay(display);
+}
+
+void
+lmb(Mousectl *mc)
+{
+	Line l;
+	Point p0, p1;
+	int i;
+
+	pts[npts] = thepoint = fromscreen(mc->xy);
+	if(++npts > 2-1){
+		theline.pts[0] = pts[0];
+		theline.pts[1] = pts[1];
+		npts = 0;
+
+		if(lines != nil){
+			free(lines);
+			lines = nil;
+			nlines = 0;
+		}
+
+		for(i = 0; i < ntiles; i++){
+			p0 = Pt(theline.pts[0].x,theline.pts[0].y);
+			p1 = Pt(theline.pts[1].x,theline.pts[1].y);
+			if(ptinrect(p0, tiles[i]))
+				paloff = i;
+//fprint(2, "%P, %P ∩ %R = ", p0, p1, tiles[i]);
+			if(rectclipline(tiles[i], &p0, &p1) < 0)
+				continue;
+//fprint(2, "%P, %P\n", p0, p1);
+			l.pts[0] = Pt2(p0.x,p0.y,1);
+			l.pts[1] = Pt2(p1.x,p1.y,1);
+			addline(l);
+		}
+	}
+}
+
+void
+rmb(Mousectl *)
+{
+	showtheline ^= 1;
+}
+
+void
+mouse(Mousectl *mc)
+{
+	static Mouse om;
+
+	if((om.buttons^mc->buttons) == 0)
+		return;
+	if((mc->buttons&1) != 0)
+		lmb(mc);
+	if((mc->buttons&4) != 0)
+		rmb(mc);
+	if((mc->buttons&8) != 0)
+		thickness = CLAMP(++thickness, 0, 10);
+	if((mc->buttons&16) != 0)
+		thickness = CLAMP(--thickness, 0, 10);
+	om = mc->Mouse;
+}
+
+void
+key(Rune r)
+{
+	switch(r){
+	case Kdel:
+	case 'q':
+		threadexitsall(nil);
+	}
+}
+
+void
+usage(void)
+{
+	fprint(2, "usage: %s tiles\n", argv0);
+	exits("usage");
+}
+
+void
+threadmain(int argc, char *argv[])
+{
+	Mousectl *mc;
+	Keyboardctl *kc;
+	Rune r;
+	int i, Δx, Δy;
+
+	ARGBEGIN{
+	default: usage();
+	}ARGEND;
+	if(argc != 1)
+		usage();
+
+	ntiles = strtoul(argv[0], nil, 10);
+	tiles = malloc(ntiles*sizeof(*tiles));
+
+	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();
+
+	worldrf.p = Pt2(screen->r.min.x,screen->r.min.y,1);
+	worldrf.bx = Vec2(1,0);
+	worldrf.by = Vec2(0,1);
+
+	Δx = Dx(screen->r);
+	Δy = Dy(screen->r)/ntiles;
+	for(i = 0; i < ntiles; i++)
+		tiles[i] = Rect(0,i*Δy,Δx,i == ntiles-1? Dy(screen->r): (i+1)*Δy);
+
+	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);
+			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.min.y,1);
+	redraw();
+}
--- a/mkfile
+++ b/mkfile
@@ -15,5 +15,6 @@
 	ptintriangle\
 	barycentric\
 	lineXcircle\
+	linetiler\
 
 </sys/src/cmd/mkmany