Add DXF export. The complexity comes from all the different ways to

specify the plane from which we want to grab the triangles. Shared
edges are then removed with the same code used to check for
watertight meshes, and the remaining edges are assembled into
polygons.

[git-p4: depot-paths = "//depot/solvespace/": change = 1823]
This commit is contained in:
Jonathan Westhues 2008-07-07 22:30:13 -08:00
parent b4a9ac993c
commit b2f2f90a27
9 changed files with 234 additions and 16 deletions

View File

@ -17,6 +17,7 @@ const GraphicsWindow::MenuEntry GraphicsWindow::menu[] = {
{ 1, "Save &As...", MNU_SAVE_AS, 0, mFile },
{ 1, NULL, 0, 0, NULL },
{ 1, "Export &Image...", MNU_EXPORT_PNG, 0, mFile },
{ 1, "Export &DXF...", MNU_EXPORT_DXF, 0, mFile },
{ 1, "Export &Mesh...", MNU_EXPORT_MESH, 0, mFile },
{ 1, NULL, 0, 0, NULL },
{ 1, "E&xit", MNU_EXIT, 0, mFile },
@ -516,7 +517,7 @@ void GraphicsWindow::MenuRequest(int id) {
case MNU_CIRCLE: s = "click center of circle"; goto c;
case MNU_ARC: s = "click point on arc (draws anti-clockwise)"; goto c;
case MNU_WORKPLANE: s = "click origin of workplane"; goto c;
case MNU_RECTANGLE: s = "click one corner of rectangular"; goto c;
case MNU_RECTANGLE: s = "click one corner of rectangle"; goto c;
case MNU_TTF_TEXT: s = "click top left of text"; goto c;
c:
SS.GW.pending.operation = id;

View File

@ -542,7 +542,7 @@ done:
if(h.v == SS.GW.activeGroup.v && SS.edgeColor != 0) {
SKdNode *root = SKdNode::From(&runningMesh);
root->SnapToMesh(&runningMesh);
root->MakeEdgesToEmphasizeInto(&emphEdges);
root->MakeCertainEdgesInto(&emphEdges, true);
}
}

View File

@ -598,7 +598,7 @@ void SKdNode::FindEdgeOn(Vector a, Vector b, int *n, int *nOther,
}
}
void SKdNode::MakeEdgesToEmphasizeInto(SEdgeList *sel) {
void SKdNode::MakeCertainEdgesInto(SEdgeList *sel, bool emphasized) {
SMesh m;
ZERO(&m);
ClearTags();
@ -616,11 +616,15 @@ void SKdNode::MakeEdgesToEmphasizeInto(SEdgeList *sel) {
int n = 0, nOther = 0;
FindEdgeOn(a, b, &n, &nOther, tr->meta, cnt++);
if(n != 1) {
dbp("hanging edge: n=%d (%.3f %.3f %.3f) (%.3f %.3f %.3f)",
if(!emphasized) {
sel->AddEdge(a, b);
} else {
dbp("hanging: n=%d (%.3f %.3f %.3f) (%.3f %.3f %.3f)",
n, CO(a), CO(b));
}
}
if(nOther > 0) {
sel->AddEdge(a, b);
if(emphasized) sel->AddEdge(a, b);
}
}
}

View File

@ -244,7 +244,7 @@ public:
void FindEdgeOn(Vector a, Vector b, int *n, int *nOther,
STriMeta m, int cnt);
void MakeEdgesToEmphasizeInto(SEdgeList *sel);
void MakeCertainEdgesInto(SEdgeList *sel, bool emphasized);
void SnapToMesh(SMesh *m);
void SnapToVertex(Vector v, SMesh *extras);

View File

@ -473,6 +473,207 @@ void SolveSpace::SolveGroup(hGroup hg) {
FreeAllTemporary();
}
void SolveSpace::ExportDxfTo(char *filename) {
SPolygon *sp;
SPolygon spa;
ZERO(&spa);
Vector gn = (SS.GW.projRight).Cross(SS.GW.projUp);
gn = gn.WithMagnitude(1);
SS.GW.GroupSelection();
#define gs (SS.GW.gs)
Group *g = SS.GetGroup(SS.GW.activeGroup);
// The plane in which the exported section lies; need this because we'll
// reorient from that plane into the xy plane before exporting.
Vector p, u, v, n;
double d;
if(gs.n == 0 && !(g->poly.IsEmpty())) {
// Easiest case--export the polygon drawn in this group
sp = &(g->poly);
p = sp->AnyPoint();
n = (sp->ComputeNormal()).WithMagnitude(1);
if(n.Dot(gn) < 0) n = n.ScaledBy(-1);
u = n.Normal(0);
v = n.Normal(1);
d = p.Dot(n);
goto havepoly;
}
if(g->runningMesh.l.n > 0 &&
((gs.n == 0 && g->activeWorkplane.v != Entity::FREE_IN_3D.v) ||
(gs.n == 1 && gs.faces == 1) ||
(gs.n == 3 && gs.vectors == 2 && gs.points == 1)))
{
if(gs.n == 0) {
Entity *wrkpl = SS.GetEntity(g->activeWorkplane);
p = wrkpl->WorkplaneGetOffset();
n = wrkpl->Normal()->NormalN();
u = wrkpl->Normal()->NormalU();
v = wrkpl->Normal()->NormalV();
} else if(gs.n == 1) {
Entity *face = SS.GetEntity(gs.entity[0]);
p = face->FaceGetPointNum();
n = face->FaceGetNormalNum();
if(n.Dot(gn) < 0) n = n.ScaledBy(-1);
u = n.Normal(0);
v = n.Normal(1);
} else if(gs.n == 3) {
Vector ut = SS.GetEntity(gs.entity[0])->VectorGetNum(),
vt = SS.GetEntity(gs.entity[1])->VectorGetNum();
ut = ut.WithMagnitude(1);
vt = vt.WithMagnitude(1);
if(fabs(SS.GW.projUp.Dot(vt)) < fabs(SS.GW.projUp.Dot(ut))) {
SWAP(Vector, ut, vt);
}
if(SS.GW.projRight.Dot(ut) < 0) ut = ut.ScaledBy(-1);
if(SS.GW.projUp. Dot(vt) < 0) vt = vt.ScaledBy(-1);
p = SS.GetEntity(gs.point[0])->PointGetNum();
n = ut.Cross(vt);
u = ut.WithMagnitude(1);
v = (n.Cross(u)).WithMagnitude(1);
} else oops();
n = n.WithMagnitude(1);
d = p.Dot(n);
SMesh m;
ZERO(&m);
m.MakeFromCopy(&(g->runningMesh));
m.l.ClearTags();
int i;
for(i = 0; i < m.l.n; i++) {
STriangle *tr = &(m.l.elem[i]);
if((fabs(n.Dot(tr->a) - d) >= LENGTH_EPS) ||
(fabs(n.Dot(tr->b) - d) >= LENGTH_EPS) ||
(fabs(n.Dot(tr->c) - d) >= LENGTH_EPS))
{
tr->tag = 1;
}
}
m.l.RemoveTagged();
SKdNode *root = SKdNode::From(&m);
root->SnapToMesh(&m);
SEdgeList el;
ZERO(&el);
root->MakeCertainEdgesInto(&el, false);
el.AssemblePolygon(&spa, NULL);
sp = &spa;
el.Clear();
m.Clear();
SS.GW.ClearSelection();
goto havepoly;
}
Error("Geometry to export not specified.");
return;
havepoly:
FILE *f = fopen(filename, "wb");
if(!f) {
Error("Couldn't write to '%s'", filename);
spa.Clear();
return;
}
// Some software, like Adobe Illustrator, insists on a header.
fprintf(f,
" 999\n"
"file created by SolveSpace\n"
" 0\n"
"SECTION\n"
" 2\n"
"HEADER\n"
" 9\n"
"$ACADVER\n"
" 1\n"
"AC1006\n"
" 9\n"
"$INSBASE\n"
" 10\n"
"0.0\n"
" 20\n"
"0.0\n"
" 30\n"
"0.0\n"
" 9\n"
"$EXTMIN\n"
" 10\n"
"0.0\n"
" 20\n"
"0.0\n"
" 9\n"
"$EXTMAX\n"
" 10\n"
"10000.0\n"
" 20\n"
"10000.0\n"
" 0\n"
"ENDSEC\n");
// Now begin the entities, which are just line segments reproduced from
// our piecewise linear curves.
fprintf(f,
" 0\n"
"SECTION\n"
" 2\n"
"ENTITIES\n");
int i, j;
for(i = 0; i < sp->l.n; i++) {
SContour *sc = &(sp->l.elem[i]);
for(j = 1; j < sc->l.n; j++) {
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);
fprintf(f,
" 0\n"
"LINE\n"
" 8\n" // Layer code
"%d\n"
" 10\n" // xA
"%.6f\n"
" 20\n" // yA
"%.6f\n"
" 30\n" // zA
"%.6f\n"
" 11\n" // xB
"%.6f\n"
" 21\n" // yB
"%.6f\n"
" 31\n" // zB
"%.6f\n",
0,
e0.x, e0.y, 0.0,
e1.x, e1.y, 0.0);
}
}
fprintf(f,
" 0\n"
"ENDSEC\n"
" 0\n"
"EOF\n" );
spa.Clear();
fclose(f);
}
void SolveSpace::ExportMeshTo(char *filename) {
SMesh *m = &(SS.GetGroup(SS.GW.activeGroup)->runningMesh);
if(m->l.n == 0) {
@ -692,6 +893,13 @@ void SolveSpace::MenuFile(int id) {
break;
}
case GraphicsWindow::MNU_EXPORT_DXF: {
char exportFile[MAX_PATH] = "";
if(!GetSaveFile(exportFile, DXF_EXT, DXF_PATTERN)) break;
SS.ExportDxfTo(exportFile);
break;
}
case GraphicsWindow::MNU_EXPORT_MESH: {
char exportFile[MAX_PATH] = "";
if(!GetSaveFile(exportFile, STL_EXT, STL_PATTERN)) break;

View File

@ -60,6 +60,8 @@ int SaveFileYesNoCancel(void);
#define PNG_EXT "png"
#define STL_PATTERN "STL Mesh (*.stl)\0*.stl\0All Files (*)\0*\0\0"
#define STL_EXT "stl"
#define DXF_PATTERN "DXF File (*.dxf)\0*.dxf\0All Files (*)\0*\0\0"
#define DXF_EXT "dxf"
BOOL GetSaveFile(char *file, char *defExtension, char *selPattern);
BOOL GetOpenFile(char *file, char *defExtension, char *selPattern);
void GetAbsoluteFilename(char *file);
@ -400,6 +402,7 @@ public:
void ReloadAllImported(void);
// And the various export options
void ExportAsPngTo(char *file);
void ExportDxfTo(char *file);
void ExportMeshTo(char *file);
void MarkGroupDirty(hGroup hg);

1
ui.h
View File

@ -149,6 +149,7 @@ public:
MNU_SAVE_AS,
MNU_EXPORT_PNG,
MNU_EXPORT_MESH,
MNU_EXPORT_DXF,
MNU_EXIT,
// View
MNU_ZOOM_IN,

View File

@ -349,20 +349,22 @@ Vector Vector::Normal(int which) {
// Arbitrarily choose one vector that's normal to us, pivoting
// appropriately.
double xa = fabs(x), ya = fabs(y), za = fabs(z);
double minc = min(min(xa, ya), za);
if(minc == xa) {
if(this->Equals(Vector::From(0, 0, 1))) {
// Make DXFs exported in the XY plane work nicely...
n = Vector::From(1, 0, 0);
} else if(xa < ya && xa < za) {
n.x = 0;
n.y = z;
n.z = -y;
} else if(minc == ya) {
} else if(ya < za) {
n.x = -z;
n.y = 0;
n.z = x;
n.x = -z;
} else if(minc == za) {
n.z = 0;
} else {
n.x = y;
n.y = -x;
} else oops();
n.z = 0;
}
if(which == 0) {
// That's the vector we return.

View File

@ -1,6 +1,5 @@
STL export
DXF export
adaptive pwl for polynomial curves
some kind of rounding / chamfer
remove back button in browser?
auto-generate circles and faces when lathing