From 5633da27ae4132550959fc7135f7d19afa519a70 Mon Sep 17 00:00:00 2001 From: rodri Date: Fri, 19 Jan 2024 16:46:32 +0000 Subject: new toy: lineXcircle. --- lineXcircle.c | 265 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mkfile | 1 + 2 files changed, 266 insertions(+) create mode 100644 lineXcircle.c diff --git a/lineXcircle.c b/lineXcircle.c new file mode 100644 index 0000000..889b3d7 --- /dev/null +++ b/lineXcircle.c @@ -0,0 +1,265 @@ +#include +#include +#include +#include +#include +#include +#include + +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(); +} diff --git a/mkfile b/mkfile index 28a83fe..d8c4ad1 100644 --- a/mkfile +++ b/mkfile @@ -14,5 +14,6 @@ TARG=\ ptinpoly\ ptintriangle\ barycentric\ + lineXcircle\