shithub: etoys

Download patch

ref: 11b5a9d404f0d53e3ba0306ea35cebe0dea534f4
parent: 1c93e86c01eaad0143ee193cf11dec5dee82b6c9
author: rodri <rgl@antares-labs.eu>
date: Sun Jun 28 12:50:26 EDT 2020

new toy: ptintriangle

--- a/mkfile
+++ b/mkfile
@@ -12,6 +12,7 @@
 	imagelerp\
 	rframeviz\
 	ptinpoly\
+	ptintriangle\
 
 HFILES=\
 	libgeometry/geometry.h\
--- /dev/null
+++ b/ptintriangle.c
@@ -1,0 +1,252 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include <draw.h>
+#include <mouse.h>
+#include <keyboard.h>
+#include <geometry.h>
+
+enum {
+	PCBg,
+	PCFg,
+	PCPoly,
+	PCPolydark,
+	PCAux,
+	NCOLORS
+};
+
+typedef struct Triangle Triangle;
+
+struct Triangle
+{
+	Point2 p0, p1, p2;
+};
+
+Rectangle UR = {0,0,1,1};	/* unit rectangle */
+RFrame worldrf;
+Image *pal[NCOLORS];
+Triangle thetriangle;
+Point2 thepoint;
+int isinside;
+
+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);
+}
+
+/*
+ * comparison of a point p with an edge [e0 e1]
+ * p to the right: +
+ * p to the left: -
+ * p on the edge: 0
+ */
+static int
+edgeptcmp(Point2 e0, Point2 e1, Point2 p)
+{
+	Point3 e0p, e01, r;
+
+	p = subpt2(p, e0);
+	e1 = subpt2(e1, e0);
+	e0p = Vec3(p.x,p.y,0);
+	e01 = Vec3(e1.x,e1.y,0);
+	r = crossvec3(e0p, e01);
+
+	/* clamp to avoid overflow */
+	return fclamp(r.z, -1, 1); /* e0.x*e1.y - e0.y*e1.x */
+}
+
+int
+ptintriangle(Point2 p, Triangle t)
+{
+	/* counter-clockwise check */
+	return edgeptcmp(t.p0, t.p2, p) <= 0 &&
+		edgeptcmp(t.p2, t.p1, p) <= 0 &&
+		edgeptcmp(t.p1, t.p0, p) <= 0; 
+}
+
+Triangle
+Trianpt(Point2 p0, Point2 p1, Point2 p2)
+{
+	return (Triangle){p0, p1, p2};
+};
+
+void
+triangle(Image *dst, Triangle t, int thick, Image *src, Point sp)
+{
+	Point pl[4];
+
+	pl[0] = toscreen(t.p0);
+	pl[1] = toscreen(t.p1);
+	pl[2] = toscreen(t.p2);
+	pl[3] = pl[0];
+
+	poly(dst, pl, nelem(pl), Endsquare, Endsquare, thick, src, sp);
+}
+
+void
+filltriangle(Image *dst, Triangle t, Image *src, Point sp)
+{
+	Point pl[3];
+
+	pl[0] = toscreen(t.p0);
+	pl[1] = toscreen(t.p1);
+	pl[2] = toscreen(t.p2);
+
+	fillpoly(dst, pl, nelem(pl), 0, src, sp);
+}
+
+void
+initpalette(void)
+{
+	pal[PCBg] = allocimage(display, UR, screen->chan, 1, DWhite);
+	pal[PCFg] = allocimage(display, UR, screen->chan, 1, DBlack);
+	pal[PCPoly] = allocimage(display, UR, screen->chan, 1, DPalebluegreen);
+	pal[PCPolydark] = allocimage(display, UR, screen->chan, 1, DDarkblue);
+	pal[PCAux] = allocimage(display, UR, screen->chan, 1, DRed);
+}
+
+void
+drawbanner(void)
+{
+	static char *msgs[] = {
+		"THE POINT IS OUTSIDE",
+		"THE POINT IS INSIDE"
+	}, *s;
+
+	s = msgs[isinside];
+	string(screen, toscreen(Pt2(Dx(screen->r)/2 - strlen(s)/2*font->width,10,1)), pal[PCFg], ZP, font, s);
+}
+
+void
+redraw(void)
+{
+	lockdisplay(display);
+	draw(screen, screen->r, pal[PCBg], nil, ZP);
+	filltriangle(screen, thetriangle, pal[PCPoly], ZP);
+	triangle(screen, thetriangle, 1, pal[PCPolydark], ZP);
+	fillellipse(screen, toscreen(thetriangle.p0), 2, 2, pal[PCPolydark], ZP);
+	fillellipse(screen, toscreen(thetriangle.p1), 2, 2, pal[PCPolydark], ZP);
+	fillellipse(screen, toscreen(thetriangle.p2), 2, 2, pal[PCPolydark], ZP);
+	fillellipse(screen, toscreen(thepoint), 2, 2, pal[PCAux], ZP);
+	drawbanner();
+	flushimage(display, 1);
+	unlockdisplay(display);
+}
+
+void
+rmb(Mousectl *mc)
+{
+	thepoint = fromscreen(mc->xy);
+	isinside = ptintriangle(thepoint, thetriangle);
+}
+
+void
+lmb(Mousectl *mc)
+{
+	USED(mc);
+}
+
+void
+mouse(Mousectl *mc)
+{
+	if((mc->buttons&1) != 0)
+		lmb(mc);
+	if((mc->buttons&4) != 0)
+		rmb(mc);
+}
+
+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;
+	Point2 tmp;
+
+	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();
+
+	worldrf.p = Pt2(screen->r.min.x,screen->r.min.y,1);
+	worldrf.bx = Vec2(1,0);
+	worldrf.by = Vec2(0,1);
+	tmp = Pt2(Dx(screen->r)/2,Dy(screen->r)/2,1);
+	thetriangle = Trianpt(tmp, addpt2(tmp, Vec2(200,0)), addpt2(tmp, Vec2(0,-160)));
+
+	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();
+}