ref: d64cf75cf23432793f4daad8e50961b9071bb44e
dir: /arcs.c/
#include <u.h> #include <libc.h> #include <bio.h> #include <draw.h> #include <memdraw.h> #define INTPATH void usage(void) { fprint(2, "usage: [-c] [-x width] [-h toolliftheight] [-z tooldrawheight] %s\n", argv0); exits("usage"); } typedef struct Position Position; struct Position { float x; float y; }; Position Pos(float x, float y) { Position r; r.x = x; r.y = y; return r; } void Psortx(Position *a, Position *b) { Position tmp; if (a->x <= b->x) return; tmp = *a; *a = *b; *b = tmp; } void Psorty(Position *a, Position *b) { Position tmp; if (a->y <= b->y) return; tmp = *a; *a = *b; *b = tmp; } int Poseq(Position a, Position b) { return a.x == b.x && a.y == b.y; } float Pdist(Position a, Position b) { return fabs(a.x - b.x) + fabs(a.y - b.y); } void Pswiz(Position *a) { float t; t = a->x; a->x = a->y; a->y = t; } Biobuf *bout; float power = 1.0; float toolliftheight = 5.; float tooldrawheight = 2.; float width = 100.; /* default 10cm */ float radscale = 10.; /* default 1cm */ Position offset = { 5., 5. }; /* default offset 5mm */ float scale; /* calculated from width */ float travelspeed = 250.; /* ender 5 max speed */ float drawspeed = 100.; /* guessed */ float zspeed = 50.; /* guessed */ float minx, maxx; float miny, maxy; int quiet = 0; int printinfo = 0; int numarcs = 0; int numcircles = 0; float traveldistance = 0.; float drawdistance = 0.; float zdistance = 0.; Position lastpos = {0., 0.}; void printheader(void) { Bprint(bout, "; generated by arcs\n"); Bprint(bout, "M203 X%g Y%g Z%g ; draw speed\n", drawspeed, drawspeed, zspeed); Bprint(bout, "G28 ; home\n"); Bprint(bout, "G90 ; absolute positions\n"); Bprint(bout, "G01 Z%g\n", toolliftheight); } float rgb2val(uchar *abgr) { float r, g, b; float rd, m; r = abgr[3] / 256.; r *= 0.2126; g = abgr[2] / 256.; g *= 0.7152; b = abgr[1] / 256.; b *= 0.0722; //return r + g + b; m = pow(r + g + b, power); /* dithering: cheap random dither for now */ rd = nrand(256) / 255.; return rd > m ? 1. : 0.; } float a2rad(uchar a) { return a / 255. * 2. - 1.; } int calcarcs(Position o, float r, float y, Position *x1, Position *x2, int inv) { float ds; float d; if (r < 0.) r *= -1.; if (inv) d = -(o.x * o.x) + 2*o.x*y + r*r - y*y; else d = -(o.y * o.y) + 2*o.y*y + r*r - y*y; if (d < 0) return 0; ds = sqrt(d); if (inv) { x1->x = y; x1->y = o.y + ds; } else { x1->x = o.x + ds; x1->y = y; } if (d == 0) { //fprint(2, "cut: tangent %g, %g (y=%g; d=%g)\n", o.x, o.y, y, d); return 1; } if (inv) { x2->x = y; x2->y = o.y - ds; } else { x2->x = o.x - ds; x2->y = y; } //fprint(2, "cut: secant %g, %g (y=%g; d=%g)\n", o.x, o.y, y, d); return 2; } int inbox(Position p) { if (p.x < minx) return 0; if (p.x > maxx) return 0; if (p.y < miny) return 0; if (p.y > maxy) return 0; return 1; } int inboxarc(Position p, Position q) { // TODO FIXME: top y border is weird (no new arcs started in that area) if (p.x == q.x && p.y == q.y) return inbox(p); if (p.x >= maxx && q.x >= maxx) if (p.y < q.y) return 0; if (p.x <= minx && q.x <= minx) if (p.y > q.y) return 0; if (p.y >= maxy && q.y >= maxy) if (p.x > q.x) return 0; if (p.y <= miny && q.y <= miny) if (p.x < q.x) return 0; return inbox(p); // needed? otherwise, 1 } void checknan(Position p, Position o, char *s) { if (isNaN(p.x)) fprint(2, "%s.x=NaN: %g, %g\n", s, o.x, o.y); if (isNaN(p.y)) fprint(2, "%s.y=NaN; %g, %g\n", s, o.x, o.y); } int genarcs(Position o, float r, Position *ps) { Position x1, x2, x3, x4, y1, y2, y3, y4; int nx1, nx2, ny1, ny2; int i = 0; float or = r; if (r < 0.) r *= -1.; nx1 = calcarcs(o, r, maxy, &x1, &x2, 0); nx2 = calcarcs(o, r, miny, &x3, &x4, 0); ny1 = calcarcs(o, r, maxx, &y1, &y2, 1); ny2 = calcarcs(o, r, minx, &y3, &y4, 1); switch (nx1) { case 0: x1 = Pos(o.x, o.y + r); case 1: x2 = x1; } switch (nx2) { case 0: x3 = Pos(o.x, o.y - r); case 1: x4 = x3; } switch (ny1) { case 0: y1 = Pos(o.x + r, o.y); case 1: y2 = y1; } switch (ny2) { case 0: y3 = Pos(o.x - r, o.y); case 1: y4 = y3; } checknan(x1, o, "x1"); checknan(x2, o, "x2"); checknan(x3, o, "x3"); checknan(x4, o, "x4"); checknan(y1, o, "y1"); checknan(y2, o, "y2"); checknan(y3, o, "y3"); checknan(y4, o, "y4"); Psortx(&x1, &x2); Psortx(&x3, &x4); Psorty(&y2, &y1); Psorty(&y4, &y3); /* x1 x2 +---x----x---+ | | y3 x x y1 | . | | | y4 x x y2 | | +---x----x---+ 0,0 x3 x4 */ if (or > 0.) { if (inboxarc(y1, x2)) { ps[i++] = y1; ps[i++] = x2; } if (inboxarc(x1, y3)) { ps[i++] = x1; ps[i++] = y3; } } else { if (inboxarc(y4, x3)) { ps[i++] = y4; ps[i++] = x3; } if (inboxarc(x4, y2)) { ps[i++] = x4; ps[i++] = y2; } } return i; } void checkpos(Position p) { if (p.x < minx) fprint(2, "Position out of range x=%g < %g\n", p.x, minx); if (p.x > maxx) fprint(2, "Position out of range x=%g > %g\n", p.x, maxx); if (p.y < miny) fprint(2, "Position out of range y=%g < %g\n", p.x, miny); if (p.y > maxy) fprint(2, "Position out of range y=%g > %g\n", p.x, maxy); } void writearc(int x, int y, uchar *abgr) { float radius; Position p; Position points[8]; int npoints; float h = rgb2val(abgr); if (h < 0.5) return; p.x = x * scale + offset.x; p.y = y * scale + offset.y; radius = a2rad(abgr[0]) * scale * radscale; if (!quiet) { int ispos = 0; /* skip z movement if not needed */ Bprint(bout, "; PX %d, %d: %d\n", x, y, abgr[0]); // TODO: back and forth mode: for fewer travel paths npoints = genarcs(p, radius, points); for (int i = 0; i < npoints; i += 2) { Position from; Position to; Position lookahead; int hasla; from = points[i]; to = points[i+1]; checkpos(from); checkpos(to); hasla = i+2 < npoints; if (hasla) lookahead = points[i+2]; if (!ispos) { Bprint(bout, "G01 X%f Y%f F%f\n", from.x, from.y, travelspeed); Bprint(bout, "G01 Z%f\n", tooldrawheight); traveldistance += Pdist(lastpos, from); zdistance += abs(tooldrawheight - toolliftheight); } Bprint(bout, "G03 X%f Y%f R%f ; %d\n", to.x, to.y, radius, i); numarcs++; // TODO: drawdistance /* skip z movement if not needed */ if (hasla && !Poseq(lookahead, to)) { Bprint(bout, "G01 Z%f\n", toolliftheight); zdistance += abs(tooldrawheight - toolliftheight); } ispos = hasla && Poseq(lookahead, to); } Bprint(bout, "G01 Z%f\n", toolliftheight); // TODO: only add if needed zdistance += abs(tooldrawheight - toolliftheight); } if (!printinfo) return; numcircles++; lastpos = p; // TODO: replace with arc calculation drawdistance += 2 * PI * radius; } #pragma varargck type "X" double int fmttime(Fmt *f) { double seconds; int hours; int minutes; seconds = va_arg(f->args, double); hours = seconds / 3600; seconds -= hours * 3600; minutes = seconds / 60; seconds -= minutes * 60; return fmtprint(f, "%dh %dm %.2fs", hours, minutes, seconds); } float printstat(int fd, char *name, float distance, float time) { fprint(fd, "%10s: %f (est: %X)\n", name, distance, time); return time; } void main(int argc, char **argv) { Memimage *img; int readcompressed = 0; uchar *b; float f; double t; double loadtime; double proctime; ARGBEGIN{ case 'c': readcompressed++; break; case 'x': width = atof(EARGF(usage())); break; case 'p': power = atof(EARGF(usage())); break; case 'r': radscale = atof(EARGF(usage())); break; case 'h': toolliftheight = atof(EARGF(usage())); break; case 'z': tooldrawheight = atof(EARGF(usage())); break; case 'i': printinfo++; break; case 'q': quiet++; break; default: usage(); }ARGEND; t = cputime(); if (memimageinit()) sysfatal("%r"); img = readcompressed ? creadmemimage(0) : readmemimage(0); if (!img) sysfatal("cannot read memimage: %r"); loadtime = cputime() - t; if (img->chan != RGBA32) sysfatal("unsupported image format"); bout = Bfdopen(1, OWRITE); if (!bout) sysfatal("%r"); fmtinstall('X', fmttime); scale = width / Dx(img->r); if (!quiet) printheader(); minx = img->r.min.x + offset.x; maxx = img->r.max.x + offset.x; miny = img->r.min.y + offset.y; maxy = img->r.max.y + offset.y; t = cputime(); for (ulong x = img->r.min.x; x < img->r.max.x; x++) { for (ulong y = img->r.min.y; y < img->r.max.y; y++) { b = byteaddr(img, Pt(x, y)); writearc(x, y, b); } } proctime = cputime() - t; if (printinfo) { fprint(2, "statistics:\n\n"); f = 0.; fprint(2, "%10s: %X\n", "loadtime", loadtime); fprint(2, "%10s: %X\n", "proctime", proctime); fprint(2, "\n"); fprint(2, "%10s: %d\n", "numcircles", numcircles); fprint(2, "%10s: %d\n", "numarcs", numarcs); f += printstat(2, "travel", traveldistance, traveldistance / travelspeed); f += printstat(2, "draw", drawdistance, drawdistance / drawspeed); f += printstat(2, "zdist", zdistance, zdistance / zspeed); fprint(2, "\n%10s: %X\n", "total", f); } exits(nil); }