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