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);
}