Split export DXF command into export section and export view, and

add framework for additional vector output formats (ps, pdf).

[git-p4: depot-paths = "//depot/solvespace/": change = 1893]
solver
Jonathan Westhues 2009-01-13 21:10:42 -08:00
parent ef11978d2c
commit b8da4ed2b3
7 changed files with 250 additions and 123 deletions

View File

@ -1,123 +1,152 @@
#include "solvespace.h"
#include <png.h>
void SolveSpace::ExportDxfTo(char *filename) {
void SolveSpace::ExportSectionTo(char *filename) {
SPolygon sp;
ZERO(&sp);
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);
if(g->runningMesh.l.n == 0) {
Error("No solid model present; draw one with extrudes and revolves, "
"or use Export 2d View to export bare lines and curves.");
return;
}
// 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;
Vector origin, u, v, n;
double d;
// Don't use the assembled polygon from the group data structure; that
// one gets cleared if the curves aren't all closed.
g->AssemblePolygon(&sp, NULL);
if(gs.n == 0 && !(sp.IsEmpty())) {
// Easiest case--export the polygon drawn in this group
p = sp.AnyPoint();
n = (sp.ComputeNormal()).WithMagnitude(1);
SS.GW.GroupSelection();
#define gs (SS.GW.gs)
if((gs.n == 0 && g->activeWorkplane.v != Entity::FREE_IN_3D.v)) {
Entity *wrkpl = SS.GetEntity(g->activeWorkplane);
origin = wrkpl->WorkplaneGetOffset();
n = wrkpl->Normal()->NormalN();
u = wrkpl->Normal()->NormalU();
v = wrkpl->Normal()->NormalV();
} else if(gs.n == 1 && gs.faces == 1) {
Entity *face = SS.GetEntity(gs.entity[0]);
origin = face->FaceGetPointNum();
n = face->FaceGetNormalNum();
if(n.Dot(gn) < 0) n = n.ScaledBy(-1);
u = n.Normal(0);
v = n.Normal(1);
d = p.Dot(n);
goto havepoly;
} else if(gs.n == 3 && gs.vectors == 2 && gs.points == 1) {
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);
origin = SS.GetEntity(gs.point[0])->PointGetNum();
n = ut.Cross(vt);
u = ut.WithMagnitude(1);
v = (n.Cross(u)).WithMagnitude(1);
} else {
Error("Bad selection for export section. Please select:\r\n\r\n"
" * nothing, with an active workplane "
"(workplane is section plane)\r\n"
" * a face (section plane through face)\r\n"
" * a point and two line segments "
"(plane through point and parallel to lines)\r\n");
return;
}
SS.GW.ClearSelection();
n = n.WithMagnitude(1);
d = origin.Dot(n);
SMesh m;
ZERO(&m);
m.MakeFromCopy(&(g->runningMesh));
// Delete all triangles in the mesh that do not lie in our export plane.
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();
// Select the naked edges in our resulting open mesh.
SKdNode *root = SKdNode::From(&m);
root->SnapToMesh(&m);
SEdgeList el;
ZERO(&el);
root->MakeCertainEdgesInto(&el, false);
// Assemble those edges into a polygon, and clear the edge list
el.AssemblePolygon(&sp, NULL);
el.Clear();
m.Clear();
// And write the polygon.
VectorFileWriter *out = VectorFileWriter::ForFile(filename);
if(out) {
ExportPolygon(&sp, u, v, n, origin, out);
}
sp.Clear();
}
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);
void SolveSpace::ExportViewTo(char *filename) {
int i;
SEdgeList edges;
ZERO(&edges);
for(i = 0; i < SS.entity.n; i++) {
Entity *e = &(SS.entity.elem[i]);
if(!e->IsVisible()) continue;
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(&sp, NULL);
el.Clear();
m.Clear();
SS.GW.ClearSelection();
goto havepoly;
e->GenerateEdges(&edges);
}
SPolygon sp;
ZERO(&sp);
edges.AssemblePolygon(&sp, NULL);
Error("Geometry to export not specified.");
return;
Vector u = SS.GW.projRight,
v = SS.GW.projUp,
n = u.Cross(v),
origin = SS.GW.offset.ScaledBy(-1);
havepoly:
VectorFileWriter *out = VectorFileWriter::ForFile(filename);
if(out) {
ExportPolygon(&sp, u, v, n, origin, out);
}
edges.Clear();
sp.Clear();
}
void SolveSpace::ExportPolygon(SPolygon *sp,
Vector u, Vector v, Vector n, Vector origin,
VectorFileWriter *out)
{
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(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->Minus(origin);
*p = p->DotInToCsys(u, v, n);
// and apply the export scale factor
double s = SS.exportScale;
*p = p->ScaledBy(1.0/s);
}
}
@ -125,20 +154,62 @@ havepoly:
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;
sp->normal = Vector::From(0, 0, -1);
sp->FixContourDirections();
sp->OffsetInto(&compd, SS.exportOffset);
sp->Clear();
*sp = compd;
}
// Now begin the entities, which are just line segments reproduced from
// our piecewise linear curves.
out->StartFile();
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;
out->LineSegment(p0.x, p0.y, p1.x, p1.y);
}
}
out->FinishAndCloseFile();
}
bool VectorFileWriter::StringEndsIn(char *str, char *ending) {
int i, ls = strlen(str), le = strlen(ending);
if(ls < le) return false;
for(i = 0; i < le; i++) {
if(tolower(ending[le-i-1]) != tolower(str[ls-i-1])) {
return false;
}
}
return true;
}
VectorFileWriter *VectorFileWriter::ForFile(char *filename) {
VectorFileWriter *ret;
if(StringEndsIn(filename, ".dxf")) {
static DxfFileWriter DxfWriter;
ret = &DxfWriter;
} else {
Error("Can't identify output file type from file extension.");
return NULL;
}
FILE *f = fopen(filename, "wb");
if(!f) {
Error("Couldn't write to '%s'", filename);
sp.Clear();
return;
return NULL;
}
ret->f = f;
return ret;
}
void DxfFileWriter::StartFile(void) {
// Some software, like Adobe Illustrator, insists on a header.
fprintf(f,
" 999\r\n"
@ -174,24 +245,19 @@ havepoly:
" 0\r\n"
"ENDSEC\r\n");
// Now begin the entities, which are just line segments reproduced from
// our piecewise linear curves.
// Then start the entities.
fprintf(f,
" 0\r\n"
"SECTION\r\n"
" 2\r\n"
"ENTITIES\r\n");
}
for(i = 0; i < sp.l.n; i++) {
SContour *sc = &(sp.l.elem[i]);
void DxfFileWriter::SetLineWidth(double mm) {
}
for(j = 1; j < sc->l.n; j++) {
Vector p0 = sc->l.elem[j-1].p,
p1 = sc->l.elem[j].p;
double s = SS.exportScale;
fprintf(f,
void DxfFileWriter::LineSegment(double x0, double y0, double x1, double y1) {
fprintf(f,
" 0\r\n"
"LINE\r\n"
" 8\r\n" // Layer code
@ -209,18 +275,16 @@ havepoly:
" 31\r\n" // zB
"%.6f\r\n",
0,
p0.x/s, p0.y/s, 0.0,
p1.x/s, p1.y/s, 0.0);
}
}
x0, y0, 0.0,
x1, y1, 0.0);
}
void DxfFileWriter::FinishAndCloseFile(void) {
fprintf(f,
" 0\r\n"
"ENDSEC\r\n"
" 0\r\n"
"EOF\r\n" );
sp.Clear();
fclose(f);
}
@ -279,8 +343,11 @@ void SolveSpace::ExportMeshTo(char *filename) {
void SolveSpace::ExportAsPngTo(char *filename) {
int w = (int)SS.GW.width, h = (int)SS.GW.height;
// No guarantee that the back buffer contains anything valid right now,
// so repaint the scene.
// so repaint the scene. And hide the toolbar too.
int prevShowToolbar = SS.showToolbar;
SS.showToolbar = false;
SS.GW.Paint(w, h);
SS.showToolbar = prevShowToolbar;
FILE *f = fopen(filename, "wb");
if(!f) goto err;

View File

@ -20,7 +20,8 @@ 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 2d &View...", MNU_EXPORT_VIEW, 0, mFile },
{ 1, "Export 2d &Section...", MNU_EXPORT_SECTION, 0, mFile },
{ 1, "Export &Mesh...", MNU_EXPORT_MESH, 0, mFile },
{ 1, NULL, 0, 0, NULL },
{ 1, "E&xit", MNU_EXIT, 0, mFile },

View File

@ -335,10 +335,17 @@ void SolveSpace::MenuFile(int id) {
break;
}
case GraphicsWindow::MNU_EXPORT_DXF: {
case GraphicsWindow::MNU_EXPORT_VIEW: {
char exportFile[MAX_PATH] = "";
if(!GetSaveFile(exportFile, DXF_EXT, DXF_PATTERN)) break;
SS.ExportDxfTo(exportFile);
SS.ExportViewTo(exportFile);
break;
}
case GraphicsWindow::MNU_EXPORT_SECTION: {
char exportFile[MAX_PATH] = "";
if(!GetSaveFile(exportFile, DXF_EXT, DXF_PATTERN)) break;
SS.ExportSectionTo(exportFile);
break;
}

View File

@ -327,6 +327,26 @@ public:
hEntity he, Vector origin, Vector u, Vector v);
};
class VectorFileWriter {
public:
FILE *f;
static bool StringEndsIn(char *str, char *ending);
static VectorFileWriter *ForFile(char *file);
virtual void SetLineWidth(double mm) = 0;
virtual void LineSegment(double x0, double y0, double x1, double y1) = 0;
virtual void StartFile(void) = 0;
virtual void FinishAndCloseFile(void) = 0;
};
class DxfFileWriter : public VectorFileWriter {
public:
void SetLineWidth(double mm);
void LineSegment(double x0, double y0, double x1, double y1);
void StartFile(void);
void FinishAndCloseFile(void);
};
class SolveSpace {
public:
TextWindow TW;
@ -438,8 +458,12 @@ public:
void ReloadAllImported(void);
// And the various export options
void ExportAsPngTo(char *file);
void ExportDxfTo(char *file);
void ExportMeshTo(char *file);
void ExportViewTo(char *file);
void ExportSectionTo(char *file);
void ExportPolygon(SPolygon *sp,
Vector u, Vector v, Vector n, Vector origin,
VectorFileWriter *out);
static void MenuAnalyze(int id);
struct {

View File

@ -5,6 +5,28 @@
class hSCurve;
class hSSurface;
// Stuff for rational polynomial curves, of degree one to three. These are
// our inputs.
class SPolyCurve {
public:
int deg;
Vector ctrl[4];
double weight[4];
Vector EvalAt(double t);
void GeneratePwlInto(SEdgeList *el);
static SPolyCurve From(Vector p0, Vector p1, Vector p2, Vector p3);
static SPolyCurve From(Vector p0, Vector p1);
};
class SPolyCurveList {
public:
List<SPolyCurve> l;
};
// Stuff for the surface trim curves: piecewise linear
class hSCurve {
public:
DWORD v;
@ -19,12 +41,16 @@ public:
hSSurface srfB;
};
// A segment of a curve by which a surface is trimmed: indicates which curve,
// by its handle, and the starting and ending points of our segment of it.
// The vector out points out of the surface; it, the surface outer normal,
// and a tangent to the beginning of the curve are all orthogonal.
class STrimBy {
public:
hSCurve curve;
Vector start;
Vector finish;
Vector out; // a vector pointing out of the contour
Vector out;
};
class hSSurface {
@ -36,15 +62,16 @@ class SSurface {
public:
hSSurface h;
int degm, degn;
Vector ctrl[4][4];
double weight[4];
double weight[4][4];
SList<STrimBy> trim;
};
class SShell {
public:
IdList<SCurve,hSCurve> allCurves;
IdList<SCurve,hSCurve> curve;
IdList<SSurface,hSSurface> surface;
};

View File

@ -660,7 +660,7 @@ 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, "%Ft cutter radius offset (in export units) ");
Printf(false, "%Ba %2 %Fl%Ll%f%D[change]%E",
(double)SS.exportOffset,
&ScreenChangeExportOffset, 0);

3
ui.h
View File

@ -178,7 +178,8 @@ public:
MNU_SAVE_AS,
MNU_EXPORT_PNG,
MNU_EXPORT_MESH,
MNU_EXPORT_DXF,
MNU_EXPORT_VIEW,
MNU_EXPORT_SECTION,
MNU_EXIT,
// View
MNU_ZOOM_IN,