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
parent
ef11978d2c
commit
b8da4ed2b3
295
export.cpp
295
export.cpp
|
@ -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;
|
||||
|
|
|
@ -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 },
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
26
solvespace.h
26
solvespace.h
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue