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:
parent
b4a9ac993c
commit
b2f2f90a27
|
@ -17,6 +17,7 @@ const GraphicsWindow::MenuEntry GraphicsWindow::menu[] = {
|
||||||
{ 1, "Save &As...", MNU_SAVE_AS, 0, mFile },
|
{ 1, "Save &As...", MNU_SAVE_AS, 0, mFile },
|
||||||
{ 1, NULL, 0, 0, NULL },
|
{ 1, NULL, 0, 0, NULL },
|
||||||
{ 1, "Export &Image...", MNU_EXPORT_PNG, 0, mFile },
|
{ 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, "Export &Mesh...", MNU_EXPORT_MESH, 0, mFile },
|
||||||
{ 1, NULL, 0, 0, NULL },
|
{ 1, NULL, 0, 0, NULL },
|
||||||
{ 1, "E&xit", MNU_EXIT, 0, mFile },
|
{ 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_CIRCLE: s = "click center of circle"; goto c;
|
||||||
case MNU_ARC: s = "click point on arc (draws anti-clockwise)"; 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_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;
|
case MNU_TTF_TEXT: s = "click top left of text"; goto c;
|
||||||
c:
|
c:
|
||||||
SS.GW.pending.operation = id;
|
SS.GW.pending.operation = id;
|
||||||
|
|
|
@ -542,7 +542,7 @@ done:
|
||||||
if(h.v == SS.GW.activeGroup.v && SS.edgeColor != 0) {
|
if(h.v == SS.GW.activeGroup.v && SS.edgeColor != 0) {
|
||||||
SKdNode *root = SKdNode::From(&runningMesh);
|
SKdNode *root = SKdNode::From(&runningMesh);
|
||||||
root->SnapToMesh(&runningMesh);
|
root->SnapToMesh(&runningMesh);
|
||||||
root->MakeEdgesToEmphasizeInto(&emphEdges);
|
root->MakeCertainEdgesInto(&emphEdges, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
12
mesh.cpp
12
mesh.cpp
|
@ -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;
|
SMesh m;
|
||||||
ZERO(&m);
|
ZERO(&m);
|
||||||
ClearTags();
|
ClearTags();
|
||||||
|
@ -616,11 +616,15 @@ void SKdNode::MakeEdgesToEmphasizeInto(SEdgeList *sel) {
|
||||||
int n = 0, nOther = 0;
|
int n = 0, nOther = 0;
|
||||||
FindEdgeOn(a, b, &n, &nOther, tr->meta, cnt++);
|
FindEdgeOn(a, b, &n, &nOther, tr->meta, cnt++);
|
||||||
if(n != 1) {
|
if(n != 1) {
|
||||||
dbp("hanging edge: n=%d (%.3f %.3f %.3f) (%.3f %.3f %.3f)",
|
if(!emphasized) {
|
||||||
n, CO(a), CO(b));
|
sel->AddEdge(a, b);
|
||||||
|
} else {
|
||||||
|
dbp("hanging: n=%d (%.3f %.3f %.3f) (%.3f %.3f %.3f)",
|
||||||
|
n, CO(a), CO(b));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if(nOther > 0) {
|
if(nOther > 0) {
|
||||||
sel->AddEdge(a, b);
|
if(emphasized) sel->AddEdge(a, b);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -244,7 +244,7 @@ public:
|
||||||
|
|
||||||
void FindEdgeOn(Vector a, Vector b, int *n, int *nOther,
|
void FindEdgeOn(Vector a, Vector b, int *n, int *nOther,
|
||||||
STriMeta m, int cnt);
|
STriMeta m, int cnt);
|
||||||
void MakeEdgesToEmphasizeInto(SEdgeList *sel);
|
void MakeCertainEdgesInto(SEdgeList *sel, bool emphasized);
|
||||||
|
|
||||||
void SnapToMesh(SMesh *m);
|
void SnapToMesh(SMesh *m);
|
||||||
void SnapToVertex(Vector v, SMesh *extras);
|
void SnapToVertex(Vector v, SMesh *extras);
|
||||||
|
|
208
solvespace.cpp
208
solvespace.cpp
|
@ -473,6 +473,207 @@ void SolveSpace::SolveGroup(hGroup hg) {
|
||||||
FreeAllTemporary();
|
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) {
|
void SolveSpace::ExportMeshTo(char *filename) {
|
||||||
SMesh *m = &(SS.GetGroup(SS.GW.activeGroup)->runningMesh);
|
SMesh *m = &(SS.GetGroup(SS.GW.activeGroup)->runningMesh);
|
||||||
if(m->l.n == 0) {
|
if(m->l.n == 0) {
|
||||||
|
@ -692,6 +893,13 @@ void SolveSpace::MenuFile(int id) {
|
||||||
break;
|
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: {
|
case GraphicsWindow::MNU_EXPORT_MESH: {
|
||||||
char exportFile[MAX_PATH] = "";
|
char exportFile[MAX_PATH] = "";
|
||||||
if(!GetSaveFile(exportFile, STL_EXT, STL_PATTERN)) break;
|
if(!GetSaveFile(exportFile, STL_EXT, STL_PATTERN)) break;
|
||||||
|
|
|
@ -60,6 +60,8 @@ int SaveFileYesNoCancel(void);
|
||||||
#define PNG_EXT "png"
|
#define PNG_EXT "png"
|
||||||
#define STL_PATTERN "STL Mesh (*.stl)\0*.stl\0All Files (*)\0*\0\0"
|
#define STL_PATTERN "STL Mesh (*.stl)\0*.stl\0All Files (*)\0*\0\0"
|
||||||
#define STL_EXT "stl"
|
#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 GetSaveFile(char *file, char *defExtension, char *selPattern);
|
||||||
BOOL GetOpenFile(char *file, char *defExtension, char *selPattern);
|
BOOL GetOpenFile(char *file, char *defExtension, char *selPattern);
|
||||||
void GetAbsoluteFilename(char *file);
|
void GetAbsoluteFilename(char *file);
|
||||||
|
@ -400,6 +402,7 @@ public:
|
||||||
void ReloadAllImported(void);
|
void ReloadAllImported(void);
|
||||||
// And the various export options
|
// And the various export options
|
||||||
void ExportAsPngTo(char *file);
|
void ExportAsPngTo(char *file);
|
||||||
|
void ExportDxfTo(char *file);
|
||||||
void ExportMeshTo(char *file);
|
void ExportMeshTo(char *file);
|
||||||
|
|
||||||
void MarkGroupDirty(hGroup hg);
|
void MarkGroupDirty(hGroup hg);
|
||||||
|
|
1
ui.h
1
ui.h
|
@ -149,6 +149,7 @@ public:
|
||||||
MNU_SAVE_AS,
|
MNU_SAVE_AS,
|
||||||
MNU_EXPORT_PNG,
|
MNU_EXPORT_PNG,
|
||||||
MNU_EXPORT_MESH,
|
MNU_EXPORT_MESH,
|
||||||
|
MNU_EXPORT_DXF,
|
||||||
MNU_EXIT,
|
MNU_EXIT,
|
||||||
// View
|
// View
|
||||||
MNU_ZOOM_IN,
|
MNU_ZOOM_IN,
|
||||||
|
|
16
util.cpp
16
util.cpp
|
@ -349,20 +349,22 @@ Vector Vector::Normal(int which) {
|
||||||
// Arbitrarily choose one vector that's normal to us, pivoting
|
// Arbitrarily choose one vector that's normal to us, pivoting
|
||||||
// appropriately.
|
// appropriately.
|
||||||
double xa = fabs(x), ya = fabs(y), za = fabs(z);
|
double xa = fabs(x), ya = fabs(y), za = fabs(z);
|
||||||
double minc = min(min(xa, ya), za);
|
if(this->Equals(Vector::From(0, 0, 1))) {
|
||||||
if(minc == xa) {
|
// 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.x = 0;
|
||||||
n.y = z;
|
n.y = z;
|
||||||
n.z = -y;
|
n.z = -y;
|
||||||
} else if(minc == ya) {
|
} else if(ya < za) {
|
||||||
|
n.x = -z;
|
||||||
n.y = 0;
|
n.y = 0;
|
||||||
n.z = x;
|
n.z = x;
|
||||||
n.x = -z;
|
} else {
|
||||||
} else if(minc == za) {
|
|
||||||
n.z = 0;
|
|
||||||
n.x = y;
|
n.x = y;
|
||||||
n.y = -x;
|
n.y = -x;
|
||||||
} else oops();
|
n.z = 0;
|
||||||
|
}
|
||||||
|
|
||||||
if(which == 0) {
|
if(which == 0) {
|
||||||
// That's the vector we return.
|
// That's the vector we return.
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
|
|
||||||
STL export
|
adaptive pwl for polynomial curves
|
||||||
DXF export
|
|
||||||
some kind of rounding / chamfer
|
some kind of rounding / chamfer
|
||||||
remove back button in browser?
|
remove back button in browser?
|
||||||
auto-generate circles and faces when lathing
|
auto-generate circles and faces when lathing
|
||||||
|
|
Loading…
Reference in New Issue
Block a user