Add cutter radius compensation. That's a bolt on thing at the end;
just applies an offset to the DXF before exporting. Useful enough to be worth the ugliness, though. This is the stupid routines from SketchFlat, slightly reworked. [git-p4: depot-paths = "//depot/solvespace/": change = 1866]
This commit is contained in:
parent
32372aee7a
commit
962cb1af4a
30
export.cpp
30
export.cpp
|
@ -110,6 +110,28 @@ void SolveSpace::ExportDxfTo(char *filename) {
|
|||
|
||||
havepoly:
|
||||
|
||||
int i, j;
|
||||
// Project into the export plane; so when we're done, z doesn't matter,
|
||||
// and x and y are what goes in the DXF.
|
||||
for(i = 0; i < sp.l.n; i++) {
|
||||
SContour *sc = &(sp.l.elem[i]);
|
||||
for(j = 0; j < sc->l.n; j++) {
|
||||
Vector *p = &(sc->l.elem[j].p);
|
||||
*p = p->DotInToCsys(u, v, n);
|
||||
}
|
||||
}
|
||||
|
||||
// If cutter radius compensation is requested, then perform it now.
|
||||
if(fabs(SS.exportOffset) > LENGTH_EPS) {
|
||||
SPolygon compd;
|
||||
ZERO(&compd);
|
||||
sp.normal = Vector::From(0, 0, -1);
|
||||
sp.FixContourDirections();
|
||||
sp.OffsetInto(&compd, SS.exportOffset);
|
||||
sp.Clear();
|
||||
sp = compd;
|
||||
}
|
||||
|
||||
FILE *f = fopen(filename, "wb");
|
||||
if(!f) {
|
||||
Error("Couldn't write to '%s'", filename);
|
||||
|
@ -160,7 +182,6 @@ havepoly:
|
|||
" 2\r\n"
|
||||
"ENTITIES\r\n");
|
||||
|
||||
int i, j;
|
||||
for(i = 0; i < sp.l.n; i++) {
|
||||
SContour *sc = &(sp.l.elem[i]);
|
||||
|
||||
|
@ -168,9 +189,6 @@ havepoly:
|
|||
Vector p0 = sc->l.elem[j-1].p,
|
||||
p1 = sc->l.elem[j].p;
|
||||
|
||||
Point2d e0 = p0.Project2d(u, v),
|
||||
e1 = p1.Project2d(u, v);
|
||||
|
||||
double s = SS.exportScale;
|
||||
|
||||
fprintf(f,
|
||||
|
@ -191,8 +209,8 @@ havepoly:
|
|||
" 31\r\n" // zB
|
||||
"%.6f\r\n",
|
||||
0,
|
||||
e0.x/s, e0.y/s, 0.0,
|
||||
e1.x/s, e1.y/s, 0.0);
|
||||
p0.x/s, p0.y/s, 0.0,
|
||||
p1.x/s, p1.y/s, 0.0);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
140
polygon.cpp
140
polygon.cpp
|
@ -383,3 +383,143 @@ void SPolygon::TriangulateInto(SMesh *m, STriMeta meta) {
|
|||
gluDeleteTess(gt);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Low-quality routines to cutter radius compensate a polygon. Assumes the
|
||||
// polygon is in the xy plane, and the contours all go in the right direction
|
||||
// with respect to normal (0, 0, -1).
|
||||
//-----------------------------------------------------------------------------
|
||||
void SPolygon::OffsetInto(SPolygon *dest, double r) {
|
||||
int i;
|
||||
dest->Clear();
|
||||
for(i = 0; i < l.n; i++) {
|
||||
SContour *sc = &(l.elem[i]);
|
||||
dest->AddEmptyContour();
|
||||
sc->OffsetInto(&(dest->l.elem[dest->l.n-1]), r);
|
||||
}
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
// Calculate the intersection point of two lines. Returns true for success,
|
||||
// false if they're parallel.
|
||||
//-----------------------------------------------------------------------------
|
||||
static bool IntersectionOfLines(double x0A, double y0A, double dxA, double dyA,
|
||||
double x0B, double y0B, double dxB, double dyB,
|
||||
double *xi, double *yi)
|
||||
{
|
||||
double A[2][2];
|
||||
double b[2];
|
||||
|
||||
// The line is given to us in the form:
|
||||
// (x(t), y(t)) = (x0, y0) + t*(dx, dy)
|
||||
// so first rewrite it as
|
||||
// (x - x0, y - y0) dot (dy, -dx) = 0
|
||||
// x*dy - x0*dy - y*dx + y0*dx = 0
|
||||
// x*dy - y*dx = (x0*dy - y0*dx)
|
||||
|
||||
// So write the matrix, pre-pivoted.
|
||||
if(fabs(dyA) > fabs(dyB)) {
|
||||
A[0][0] = dyA; A[0][1] = -dxA; b[0] = x0A*dyA - y0A*dxA;
|
||||
A[1][0] = dyB; A[1][1] = -dxB; b[1] = x0B*dyB - y0B*dxB;
|
||||
} else {
|
||||
A[1][0] = dyA; A[1][1] = -dxA; b[1] = x0A*dyA - y0A*dxA;
|
||||
A[0][0] = dyB; A[0][1] = -dxB; b[0] = x0B*dyB - y0B*dxB;
|
||||
}
|
||||
|
||||
// Check the determinant; if it's zero then no solution.
|
||||
if(fabs(A[0][0]*A[1][1] - A[0][1]*A[1][0]) < LENGTH_EPS) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Solve
|
||||
double v = A[1][0] / A[0][0];
|
||||
A[1][0] -= A[0][0]*v;
|
||||
A[1][1] -= A[0][1]*v;
|
||||
b[1] -= b[0]*v;
|
||||
|
||||
// Back-substitute.
|
||||
*yi = b[1] / A[1][1];
|
||||
*xi = (b[0] - A[0][1]*(*yi)) / A[0][0];
|
||||
|
||||
return true;
|
||||
}
|
||||
void SContour::OffsetInto(SContour *dest, double r) {
|
||||
int i;
|
||||
|
||||
for(i = 0; i < l.n; i++) {
|
||||
Vector a, b, c;
|
||||
Vector dp, dn;
|
||||
double thetan, thetap;
|
||||
|
||||
a = l.elem[WRAP(i-1, (l.n-1))].p;
|
||||
b = l.elem[WRAP(i, (l.n-1))].p;
|
||||
c = l.elem[WRAP(i+1, (l.n-1))].p;
|
||||
|
||||
dp = a.Minus(b);
|
||||
thetap = atan2(dp.y, dp.x);
|
||||
|
||||
dn = b.Minus(c);
|
||||
thetan = atan2(dn.y, dn.x);
|
||||
|
||||
// A short line segment in a badly-generated polygon might look
|
||||
// okay but screw up our sense of direction.
|
||||
if(dp.Magnitude() < LENGTH_EPS || dn.Magnitude() < LENGTH_EPS) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if(thetan > thetap && (thetan - thetap) > PI) {
|
||||
thetap += 2*PI;
|
||||
}
|
||||
if(thetan < thetap && (thetap - thetan) > PI) {
|
||||
thetan += 2*PI;
|
||||
}
|
||||
|
||||
if(fabs(thetan - thetap) < (1*PI)/180) {
|
||||
Vector p = { b.x - r*sin(thetap), b.y + r*cos(thetap) };
|
||||
dest->AddPoint(p);
|
||||
} else if(thetan < thetap) {
|
||||
// This is an inside corner. We have two edges, Ep and En. Move
|
||||
// out from their intersection by radius, normal to En, and
|
||||
// then draw a line parallel to En. Move out from their
|
||||
// intersection by radius, normal to Ep, and then draw a second
|
||||
// line parallel to Ep. The point that we want to generate is
|
||||
// the intersection of these two lines--it removes as much
|
||||
// material as we can without removing any that we shouldn't.
|
||||
double px0, py0, pdx, pdy;
|
||||
double nx0, ny0, ndx, ndy;
|
||||
double x, y;
|
||||
|
||||
px0 = b.x - r*sin(thetap);
|
||||
py0 = b.y + r*cos(thetap);
|
||||
pdx = cos(thetap);
|
||||
pdy = sin(thetap);
|
||||
|
||||
nx0 = b.x - r*sin(thetan);
|
||||
ny0 = b.y + r*cos(thetan);
|
||||
ndx = cos(thetan);
|
||||
ndy = sin(thetan);
|
||||
|
||||
IntersectionOfLines(px0, py0, pdx, pdy,
|
||||
nx0, ny0, ndx, ndy,
|
||||
&x, &y);
|
||||
|
||||
dest->AddPoint(Vector::From(x, y, 0));
|
||||
} else {
|
||||
if(fabs(thetap - thetan) < (6*PI)/180) {
|
||||
Vector pp = { b.x - r*sin(thetap),
|
||||
b.y + r*cos(thetap), 0 };
|
||||
dest->AddPoint(pp);
|
||||
|
||||
Vector pn = { b.x - r*sin(thetan),
|
||||
b.y + r*cos(thetan), 0 };
|
||||
dest->AddPoint(pn);
|
||||
} else {
|
||||
double theta;
|
||||
for(theta = thetap; theta <= thetan; theta += (6*PI)/180) {
|
||||
Vector p = { b.x - r*sin(theta),
|
||||
b.y + r*cos(theta), 0 };
|
||||
dest->AddPoint(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -89,6 +89,7 @@ public:
|
|||
bool IsClockwiseProjdToNormal(Vector n);
|
||||
bool ContainsPointProjdToNormal(Vector n, Vector p);
|
||||
bool AllPointsInPlane(Vector n, double d, Vector *notCoplanarAt);
|
||||
void OffsetInto(SContour *dest, double r);
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
|
@ -113,6 +114,7 @@ public:
|
|||
bool AllPointsInPlane(Vector *notCoplanarAt);
|
||||
bool IsEmpty(void);
|
||||
Vector AnyPoint(void);
|
||||
void OffsetInto(SPolygon *dest, double r);
|
||||
};
|
||||
|
||||
class STriangle {
|
||||
|
|
|
@ -49,6 +49,8 @@ void SolveSpace::Init(char *cmdLine) {
|
|||
edgeColor = CnfThawDWORD(RGB(0, 0, 0), "EdgeColor");
|
||||
// Export scale factor
|
||||
exportScale = CnfThawFloat(1.0f, "ExportScale");
|
||||
// Export offset (cutter radius comp)
|
||||
exportOffset = CnfThawFloat(0.0f, "ExportOffset");
|
||||
// Draw back faces of triangles (when mesh is leaky/self-intersecting)
|
||||
drawBackFaces = CnfThawDWORD(1, "DrawBackFaces");
|
||||
// Recent files menus
|
||||
|
@ -109,6 +111,8 @@ void SolveSpace::Exit(void) {
|
|||
CnfFreezeDWORD(edgeColor, "EdgeColor");
|
||||
// Export scale (a float, stored as a DWORD)
|
||||
CnfFreezeFloat(exportScale, "ExportScale");
|
||||
// Export offset (cutter radius comp)
|
||||
CnfFreezeFloat(exportOffset, "ExportOffset");
|
||||
// Draw back faces of triangles (when mesh is leaky/self-intersecting)
|
||||
CnfFreezeDWORD(drawBackFaces, "DrawBackFaces");
|
||||
|
||||
|
|
|
@ -370,6 +370,7 @@ public:
|
|||
double cameraTangent;
|
||||
DWORD edgeColor;
|
||||
float exportScale;
|
||||
float exportOffset;
|
||||
int drawBackFaces;
|
||||
|
||||
int CircleSides(double r);
|
||||
|
|
|
@ -595,6 +595,13 @@ void TextWindow::ScreenChangeExportScale(int link, DWORD v) {
|
|||
ShowTextEditControl(59, 3, str);
|
||||
SS.TW.edit.meaning = EDIT_EXPORT_SCALE;
|
||||
}
|
||||
void TextWindow::ScreenChangeExportOffset(int link, DWORD v) {
|
||||
char str[1024];
|
||||
sprintf(str, "%.2f", (double)SS.exportOffset);
|
||||
|
||||
ShowTextEditControl(63, 3, str);
|
||||
SS.TW.edit.meaning = EDIT_EXPORT_OFFSET;
|
||||
}
|
||||
void TextWindow::ScreenChangeBackFaces(int link, DWORD v) {
|
||||
SS.drawBackFaces = !SS.drawBackFaces;
|
||||
InvalidateGraphics();
|
||||
|
@ -652,6 +659,10 @@ void TextWindow::ShowConfiguration(void) {
|
|||
Printf(false, "%Ba %3 %Fl%Ll%f%D[change]%E",
|
||||
(double)SS.exportScale,
|
||||
&ScreenChangeExportScale, 0);
|
||||
Printf(false, "%Ft cutter radius offset (always in mm) ");
|
||||
Printf(false, "%Ba %2 %Fl%Ll%f%D[change]%E",
|
||||
(double)SS.exportOffset,
|
||||
&ScreenChangeExportOffset, 0);
|
||||
|
||||
Printf(false, "");
|
||||
Printf(false, "%Ft draw back faces: "
|
||||
|
@ -871,6 +882,20 @@ void TextWindow::EditControlDone(char *s) {
|
|||
}
|
||||
break;
|
||||
}
|
||||
case EDIT_EXPORT_OFFSET: {
|
||||
Expr *e = Expr::From(s);
|
||||
if(e) {
|
||||
double ev = e->Eval();
|
||||
if(isnan(ev) || ev < 0) {
|
||||
Error("Cutter radius offset must not be negative!");
|
||||
} else {
|
||||
SS.exportOffset = (float)ev;
|
||||
}
|
||||
} else {
|
||||
Error("Not a valid number or expression: '%s'", s);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case EDIT_HELIX_TURNS:
|
||||
case EDIT_HELIX_PITCH:
|
||||
case EDIT_HELIX_DRADIUS: {
|
||||
|
|
2
ui.h
2
ui.h
|
@ -75,6 +75,7 @@ public:
|
|||
static const int EDIT_CAMERA_TANGENT = 15;
|
||||
static const int EDIT_EDGE_COLOR = 16;
|
||||
static const int EDIT_EXPORT_SCALE = 17;
|
||||
static const int EDIT_EXPORT_OFFSET = 18;
|
||||
// For the helical sweep
|
||||
static const int EDIT_HELIX_TURNS = 20;
|
||||
static const int EDIT_HELIX_PITCH = 21;
|
||||
|
@ -158,6 +159,7 @@ public:
|
|||
static void ScreenChangeCameraTangent(int link, DWORD v);
|
||||
static void ScreenChangeEdgeColor(int link, DWORD v);
|
||||
static void ScreenChangeExportScale(int link, DWORD v);
|
||||
static void ScreenChangeExportOffset(int link, DWORD v);
|
||||
|
||||
void EditControlDone(char *s);
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue
Block a user