shithub: etoys

Download patch

ref: 5633da27ae4132550959fc7135f7d19afa519a70
parent: a829fc8b5a2ab396fe46ffbe8c01dcdb9d3038f6
author: rodri <rgl@antares-labs.eu>
date: Fri Jan 19 11:46:32 EST 2024

new toy: lineXcircle.

--- /dev/null
+++ b/lineXcircle.c
@@ -1,0 +1,265 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include <draw.h>
+#include <mouse.h>
+#include <keyboard.h>
+#include <geometry.h>
+
+enum {
+	PCBg,
+	PCFg,
+	PCLine,
+	PCLineseg,
+	PCCin,
+	PCCout,
+	PCPt,
+	NCOLORS
+};
+
+Rectangle UR = {0,0,1,1};	/* unit rectangle */
+RFrame worldrf;
+Image *pal[NCOLORS];
+Point2 theline[2], thecircle[2];
+int npts;
+Point2 Xpts[2];
+int nXpts;
+
+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);
+}
+
+double
+sgn(double n)
+{
+	return n < 0? -1: 1;
+}
+
+int
+lineXcircle(Point2 *r)
+{
+	Point2 p0, p1, dp;
+	double dr, D, cr, Δ;
+
+	p0 = subpt2(theline[0], thecircle[0]);
+	p1 = subpt2(theline[1], thecircle[0]);
+	dp = subpt2(p1, p0);
+	dr = vec2len(dp);
+	D = p0.x*p1.y - p0.y*p1.x;
+	cr = vec2len(subpt2(thecircle[1], thecircle[0]));
+	Δ = cr*cr*dr*dr - D*D;
+
+	if(Δ < 0)		/* imaginary */
+		return 0;
+	else if(Δ == 0){	/* tangent */
+		r[0].x = (D*dp.y + sgn(dp.y)*dp.x*sqrt(Δ))/(dr*dr);
+		r[0].y = (-D*dp.x + fabs(dp.y)*sqrt(Δ))/(dr*dr);
+		r[0] = addpt2(r[0], thecircle[0]);
+		return 1;
+	}else{			/* secant */
+		r[0].x = (D*dp.y + sgn(dp.y)*dp.x*sqrt(Δ))/(dr*dr);
+		r[0].y = (-D*dp.x + fabs(dp.y)*sqrt(Δ))/(dr*dr);
+		r[0] = addpt2(r[0], thecircle[0]);
+		r[1].x = (D*dp.y - sgn(dp.y)*dp.x*sqrt(Δ))/(dr*dr);
+		r[1].y = (-D*dp.x - fabs(dp.y)*sqrt(Δ))/(dr*dr);
+		r[1] = addpt2(r[1], thecircle[0]);
+		return 2;
+	}
+}
+
+void
+initpalette(void)
+{
+	pal[PCBg] = allocimage(display, UR, screen->chan, 1, DWhite);
+	pal[PCFg] = allocimage(display, UR, screen->chan, 1, DBlack);
+	pal[PCLine] = allocimage(display, UR, screen->chan, 1, 0xEEEEEEFF);
+	pal[PCLineseg] = allocimage(display, UR, screen->chan, 1, DDarkblue);
+	pal[PCCin] = allocimage(display, UR, screen->chan, 1, DPalebluegreen);
+	pal[PCCout] = allocimage(display, UR, screen->chan, 1, DDarkyellow);
+	pal[PCPt] = allocimage(display, UR, screen->chan, 1, DRed);
+}
+
+void
+drawline(void)
+{
+	Point pts[2], dp;
+	int i;
+
+	for(i = 0; i < nelem(pts); i++) pts[i] = toscreen(theline[i]);
+	dp = subpt(pts[1], pts[0]);
+
+	line(screen, pts[0], pts[1], 0, 0, 1, pal[PCLineseg], ZP);
+	for(i = 0; i < nelem(pts); i++){
+		line(screen, pts[i], addpt(mulpt(dp, i == 0? -10: 10), pts[i]), 0, 0, 0, pal[PCLine], ZP);
+		fillellipse(screen, pts[i], 2, 2, pal[PCPt], ZP);
+	}
+}
+
+void
+drawcircle(void)
+{
+	Point p;
+	double r;
+
+	p = toscreen(thecircle[0]);
+	r = vec2len(subpt2(thecircle[1], thecircle[0]));
+
+	fillellipse(screen, p, r, r, pal[PCCin], ZP);
+	ellipse(screen, p, r, r, 0, pal[PCCout], ZP);
+	fillellipse(screen, p, 2, 2, pal[PCPt], ZP);
+}
+
+void
+drawX(void)
+{
+	int i;
+
+	if(nXpts < 1)
+		return;
+
+	for(i = 0; i < nelem(Xpts); i++)
+		fillellipse(screen, toscreen(Xpts[i]), 2, 2, pal[PCPt], ZP);
+}
+
+void
+redraw(void)
+{
+	lockdisplay(display);
+	draw(screen, screen->r, pal[PCBg], nil, ZP);
+	if(npts >= 4){
+		drawcircle();
+		drawX();
+	}
+	if(npts >= 2)
+		drawline();
+	flushimage(display, 1);
+	unlockdisplay(display);
+}
+
+void
+rmb(Mousectl *mc)
+{
+	USED(mc);
+}
+
+void
+lmb(Mousectl *mc)
+{
+	if(npts >= 4)
+		npts = 0;
+	if(npts >= 0 && npts < 2)
+		theline[npts++] = fromscreen(mc->xy);
+	else if(npts >= 2 && npts < 4)
+		thecircle[npts++ & 1] = fromscreen(mc->xy);
+	if(npts >= 4)
+		nXpts = lineXcircle(Xpts);
+}
+
+void
+mouse(Mousectl *mc)
+{
+	static Mouse om;
+
+	if(mc->buttons == om.buttons)
+		return;
+
+	if((mc->buttons&1) != 0)
+		lmb(mc);
+	if((mc->buttons&4) != 0)
+		rmb(mc);
+	om = mc->Mouse;
+}
+
+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;
+
+	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);
+
+	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
@@ -14,5 +14,6 @@
 	ptinpoly\
 	ptintriangle\
 	barycentric\
+	lineXcircle\
 
 </sys/src/cmd/mkmany