Add eps, hpgl, and svg output (simple, all just for polylines). And

fix convergence tolerance so that points projected into a rational
polynomial surface end up much closer than LENGTH_EPS.

[git-p4: depot-paths = "//depot/solvespace/": change = 1906]
This commit is contained in:
Jonathan Westhues 2009-01-26 21:48:40 -08:00
parent 07ddd62a3a
commit 95bded27ee
4 changed files with 188 additions and 24 deletions

View File

@ -162,18 +162,7 @@ void SolveSpace::ExportPolygon(SPolygon *sp,
// 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();
out->OutputPolygon(sp);
}
bool VectorFileWriter::StringEndsIn(char *str, char *ending) {
@ -194,8 +183,18 @@ VectorFileWriter *VectorFileWriter::ForFile(char *filename) {
if(StringEndsIn(filename, ".dxf")) {
static DxfFileWriter DxfWriter;
ret = &DxfWriter;
} else if(StringEndsIn(filename, ".ps") || StringEndsIn(filename, ".eps")) {
static EpsFileWriter EpsWriter;
ret = &EpsWriter;
} else if(StringEndsIn(filename, ".svg")) {
static SvgFileWriter SvgWriter;
ret = &SvgWriter;
} else if(StringEndsIn(filename, ".plt")||StringEndsIn(filename, ".hpgl")) {
static HpglFileWriter HpglWriter;
ret = &HpglWriter;
} else {
Error("Can't identify output file type from file extension.");
Error("Can't identify output file type from file extension of "
"filename '%s'; try .dxf, .svg, .plt, .hpgl, .eps, or .ps.", filename);
return NULL;
}
@ -208,6 +207,36 @@ VectorFileWriter *VectorFileWriter::ForFile(char *filename) {
return ret;
}
void VectorFileWriter::OutputPolygon(SPolygon *sp) {
int i, j;
// First calculate the bounding box.
ptMin = Vector::From(VERY_POSITIVE, VERY_POSITIVE, VERY_POSITIVE);
ptMax = Vector::From(VERY_NEGATIVE, VERY_NEGATIVE, VERY_NEGATIVE);
for(i = 0; i < sp->l.n; i++) {
SContour *sc = &(sp->l.elem[i]);
for(j = 0; j < sc->l.n; j++) {
(sc->l.elem[j].p).MakeMaxMin(&ptMax, &ptMin);
}
}
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;
LineSegment(p0.x, p0.y, p1.x, p1.y);
}
}
FinishAndCloseFile();
}
//-----------------------------------------------------------------------------
// Routines for DXF export
//-----------------------------------------------------------------------------
void DxfFileWriter::StartFile(void) {
// Some software, like Adobe Illustrator, insists on a header.
fprintf(f,
@ -252,9 +281,6 @@ void DxfFileWriter::StartFile(void) {
"ENTITIES\r\n");
}
void DxfFileWriter::SetLineWidth(double mm) {
}
void DxfFileWriter::LineSegment(double x0, double y0, double x1, double y1) {
fprintf(f,
" 0\r\n"
@ -287,6 +313,109 @@ void DxfFileWriter::FinishAndCloseFile(void) {
fclose(f);
}
//-----------------------------------------------------------------------------
// Routines for EPS output
//-----------------------------------------------------------------------------
double EpsFileWriter::MmToPoints(double mm) {
// 72 points in an inch
return (mm/25.4)*72;
}
void EpsFileWriter::StartFile(void) {
fprintf(f,
"%%!PS-Adobe-2.0\r\n"
"%%%%Creator: SolveSpace\r\n"
"%%%%Title: title\r\n"
"%%%%Pages: 0\r\n"
"%%%%PageOrder: Ascend\r\n"
"%%%%BoundingBox: 0 0 %d %d\r\n"
"%%%%HiResBoundingBox: 0 0 %.3f %.3f\r\n"
"%%%%EndComments\r\n"
"\r\n"
"gsave\r\n"
"\r\n",
(int)ceil(MmToPoints(ptMax.x - ptMin.x)),
(int)ceil(MmToPoints(ptMax.y - ptMin.y)),
MmToPoints(ptMax.x - ptMin.x),
MmToPoints(ptMax.y - ptMin.y));
}
void EpsFileWriter::LineSegment(double x0, double y0, double x1, double y1) {
fprintf(f,
"newpath\r\n"
" %.3f %.3f moveto\r\n"
" %.3f %.3f lineto\r\n"
" 1 setlinewidth\r\n"
" 0 setgray\r\n"
"stroke\r\n",
MmToPoints(x0 - ptMin.x), MmToPoints(y0 - ptMin.y),
MmToPoints(x1 - ptMin.x), MmToPoints(y1 - ptMin.y));
}
void EpsFileWriter::FinishAndCloseFile(void) {
fprintf(f,
"\r\n"
"grestore\r\n"
"\r\n");
fclose(f);
}
//-----------------------------------------------------------------------------
// Routines for SVG output
//-----------------------------------------------------------------------------
void SvgFileWriter::StartFile(void) {
fprintf(f,
"<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.0//EN\" "
"\"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\">\r\n"
"<svg xmlns=\"http://www.w3.org/2000/svg\" "
"xmlns:xlink=\"http://www.w3.org/1999/xlink\" "
"width='%.3fmm' height='%.3fmm' "
"viewBox=\"0 0 %.3f %.3f\">\r\n"
"\r\n"
"<title>Exported SVG</title>\r\n"
"\r\n",
ptMax.x - ptMin.x, ptMax.y - ptMin.y,
ptMax.x - ptMin.x, ptMax.y - ptMin.y);
}
void SvgFileWriter::LineSegment(double x0, double y0, double x1, double y1) {
fprintf(f,
"<polyline points='%.3f %.3f, %.3f %.3f' "
"stroke-width='1' stroke='black' style='fill: none;' />\r\n",
(x0 - ptMin.x), (y0 - ptMin.y),
(x1 - ptMin.x), (y1 - ptMin.y));
}
void SvgFileWriter::FinishAndCloseFile(void) {
fprintf(f, "\r\n</svg>\r\n");
fclose(f);
}
//-----------------------------------------------------------------------------
// Routines for HPGL output
//-----------------------------------------------------------------------------
double HpglFileWriter::MmToHpglUnits(double mm) {
return mm*40;
}
void HpglFileWriter::StartFile(void) {
fprintf(f, "IN;\r\n");
fprintf(f, "SP1;\r\n");
}
void HpglFileWriter::LineSegment(double x0, double y0, double x1, double y1) {
fprintf(f, "PU%d,%d;\r\n", (int)MmToHpglUnits(x0), (int)MmToHpglUnits(y0));
fprintf(f, "PD%d,%d;\r\n", (int)MmToHpglUnits(x1), (int)MmToHpglUnits(y1));
}
void HpglFileWriter::FinishAndCloseFile(void) {
fclose(f);
}
//-----------------------------------------------------------------------------
// Export the mesh as an STL file; it should always be vertex-to-vertex and
// not self-intersecting, so not much to do.
//-----------------------------------------------------------------------------
void SolveSpace::ExportMeshTo(char *filename) {
SMesh *m = &(SS.GetGroup(SS.GW.activeGroup)->runningMesh);
if(m->l.n == 0) {
@ -332,6 +461,10 @@ void SolveSpace::ExportMeshTo(char *filename) {
fclose(f);
}
//-----------------------------------------------------------------------------
// Export a view of the model as an image; we just take a screenshot, by
// rendering the view in the usual way and then copying the pixels.
//-----------------------------------------------------------------------------
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,

View File

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

View File

@ -32,7 +32,9 @@ inline double WRAP_NOT_0(double v, double n) {
#define ZERO(v) memset((v), 0, sizeof(*(v)))
#define CO(v) (v).x, (v).y, (v).z
#define LENGTH_EPS (1e-7)
#define LENGTH_EPS (1e-7)
#define VERY_POSITIVE (1e10)
#define VERY_NEGATIVE (-1e10)
#define isforname(c) (isalnum(c) || (c) == '_' || (c) == '-' || (c) == '#')
@ -68,8 +70,12 @@ 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"
#define VEC_PATTERN "DXF File (*.dxf)\0*.dxf\0" \
"Encapsulated PostScript (*.eps;*.ps)\0*.eps;*.ps\0" \
"Scalable Vector Graphics (*.svg)\0*.svg\0" \
"HPGL File (*.plt;*.hpgl)\0*.plt;*.hpgl\0" \
"All Files (*)\0*\0\0"
#define VEC_EXT "dxf"
#define CSV_PATTERN "CSV File (*.csv)\0*.csv\0All Files (*)\0*\0\0"
#define CSV_EXT "csv"
#define LICENSE_PATTERN \
@ -331,18 +337,39 @@ public:
class VectorFileWriter {
public:
FILE *f;
Vector ptMin, ptMax;
static bool StringEndsIn(char *str, char *ending);
static VectorFileWriter *ForFile(char *file);
virtual void SetLineWidth(double mm) = 0;
void OutputPolygon(SPolygon *sp);
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 EpsFileWriter : public VectorFileWriter {
public:
static double MmToPoints(double mm);
void LineSegment(double x0, double y0, double x1, double y1);
void StartFile(void);
void FinishAndCloseFile(void);
};
class SvgFileWriter : public VectorFileWriter {
public:
void LineSegment(double x0, double y0, double x1, double y1);
void StartFile(void);
void FinishAndCloseFile(void);
};
class HpglFileWriter : public VectorFileWriter {
public:
static double MmToHpglUnits(double mm);
void LineSegment(double x0, double y0, double x1, double y1);
void StartFile(void);
void FinishAndCloseFile(void);

View File

@ -518,7 +518,11 @@ void SSurface::ClosestPointTo(Vector p, double *u, double *v) {
Vector p0;
for(i = 0; i < 50; i++) {
p0 = PointAt(*u, *v);
if(p0.Equals(p)) {
// Converge it to better than LENGTH_EPS; we want two points, each
// independently projected into uv and back, to end up equal with
// the LENGTH_EPS. Best case that requires LENGTH_EPS/2, but more
// is better and convergence should be fast by now.
if(p0.Equals(p, LENGTH_EPS/100)) {
return;
}