Add 3d wireframe export, in DXF or STEP format. This uses most of

the same plumbing as the 2d vector output.

Also fix piecewise linear tolerance when the export scale factor is
not equal to one; have to scale the chord tol along with that.

[git-p4: depot-paths = "//depot/solvespace/": change = 2053]
This commit is contained in:
Jonathan Westhues 2009-10-12 02:40:48 -08:00
parent 730bb8f73e
commit 83bbc004dc
8 changed files with 123 additions and 80 deletions

View File

@ -107,7 +107,7 @@ void SolveSpace::ExportSectionTo(char *filename) {
bl.Clear();
}
void SolveSpace::ExportViewTo(char *filename) {
void SolveSpace::ExportViewOrWireframeTo(char *filename, bool wireframe) {
int i;
SEdgeList edges;
ZERO(&edges);
@ -158,6 +158,12 @@ void SolveSpace::ExportViewTo(char *filename) {
}
}
if(wireframe) {
VectorFileWriter *out = VectorFileWriter::ForFile(filename);
if(out) {
ExportWireframeCurves(&edges, &beziers, out);
}
} else {
Vector u = SS.GW.projRight,
v = SS.GW.projUp,
n = u.Cross(v),
@ -169,10 +175,25 @@ void SolveSpace::ExportViewTo(char *filename) {
u, v, n, origin, SS.CameraTangent()*SS.GW.scale,
out);
}
}
edges.Clear();
beziers.Clear();
}
void SolveSpace::ExportWireframeCurves(SEdgeList *sel, SBezierList *sbl,
VectorFileWriter *out)
{
sbl->ScaleSelfBy(1.0/SS.exportScale);
SEdge *se;
for(se = sel->l.First(); se; se = sel->l.NextAfter(se)) {
se->a = (se->a).ScaledBy(1.0/SS.exportScale);
se->b = (se->b).ScaledBy(1.0/SS.exportScale);
}
out->Output(sel, sbl, NULL);
}
void SolveSpace::ExportLinesAndMesh(SEdgeList *sel, SBezierList *sbl, SMesh *sm,
Vector u, Vector v, Vector n,
Vector origin, double cameraTan,
@ -465,7 +486,7 @@ void VectorFileWriter::Output(SEdgeList *sel, SBezierList *sbl, SMesh *sm) {
DWORD rgb = Style::Color (e->auxA, true);
double w = Style::WidthMm(e->auxA)*s;
LineSegment(rgb, w, e->a.x, e->a.y, e->b.x, e->b.y);
LineSegment(rgb, w, e->a, e->b);
}
}
if(sbl) {
@ -483,11 +504,10 @@ void VectorFileWriter::Output(SEdgeList *sel, SBezierList *sbl, SMesh *sm) {
void VectorFileWriter::BezierAsPwl(DWORD rgb, double width, SBezier *sb) {
List<Vector> lv;
ZERO(&lv);
sb->MakePwlInto(&lv);
sb->MakePwlInto(&lv, SS.ChordTolMm() / SS.exportScale);
int i;
for(i = 1; i < lv.n; i++) {
LineSegment(rgb, width, lv.elem[i-1].x, lv.elem[i-1].y,
lv.elem[i ].x, lv.elem[i ].y);
LineSegment(rgb, width, lv.elem[i-1], lv.elem[i]);
}
lv.Clear();
}

View File

@ -62,9 +62,7 @@ void DxfFileWriter::StartFile(void) {
"ENTITIES\r\n");
}
void DxfFileWriter::LineSegment(DWORD rgb, double w,
double x0, double y0, double x1, double y1)
{
void DxfFileWriter::LineSegment(DWORD rgb, double w, Vector ptA, Vector ptB) {
fprintf(f,
" 0\r\n"
"LINE\r\n"
@ -83,8 +81,8 @@ void DxfFileWriter::LineSegment(DWORD rgb, double w,
" 31\r\n" // zB
"%.6f\r\n",
0,
x0, y0, 0.0,
x1, y1, 0.0);
ptA.x, ptA.y, ptA.z,
ptB.x, ptB.y, ptB.z);
}
void DxfFileWriter::Triangle(STriangle *tr) {
@ -93,7 +91,7 @@ void DxfFileWriter::Triangle(STriangle *tr) {
void DxfFileWriter::Bezier(DWORD rgb, double w, SBezier *sb) {
Vector c, n = Vector::From(0, 0, 1);
double r;
if(sb->IsCircle(n, &c, &r)) {
if(sb->IsInPlane(n, 0) && sb->IsCircle(n, &c, &r)) {
double theta0 = atan2(sb->ctrl[0].y - c.y, sb->ctrl[0].x - c.x),
theta1 = atan2(sb->ctrl[2].y - c.y, sb->ctrl[2].x - c.x),
dtheta = WRAP_SYMMETRIC(theta1 - theta0, 2*PI);
@ -169,17 +167,15 @@ void EpsFileWriter::StartFile(void) {
MmToPts(ptMax.y - ptMin.y));
}
void EpsFileWriter::LineSegment(DWORD rgb, double w,
double x0, double y0, double x1, double y1)
{
void EpsFileWriter::LineSegment(DWORD rgb, double w, Vector ptA, Vector ptB) {
fprintf(f,
"newpath\r\n"
" %.3f %.3f moveto\r\n"
" %.3f %.3f lineto\r\n"
"%s"
"stroke\r\n",
MmToPts(x0 - ptMin.x), MmToPts(y0 - ptMin.y),
MmToPts(x1 - ptMin.x), MmToPts(y1 - ptMin.y),
MmToPts(ptA.x - ptMin.x), MmToPts(ptA.y - ptMin.y),
MmToPts(ptB.x - ptMin.x), MmToPts(ptB.y - ptMin.y),
StyleString(rgb, w));
}
@ -396,17 +392,15 @@ void PdfFileWriter::FinishAndCloseFile(void) {
}
void PdfFileWriter::LineSegment(DWORD rgb, double w,
double x0, double y0, double x1, double y1)
{
void PdfFileWriter::LineSegment(DWORD rgb, double w, Vector ptA, Vector ptB) {
fprintf(f,
"%s"
"%.3f %.3f m\r\n"
"%.3f %.3f l\r\n"
"S\r\n",
StyleString(rgb, w),
MmToPts(x0 - ptMin.x), MmToPts(y0 - ptMin.y),
MmToPts(x1 - ptMin.x), MmToPts(y1 - ptMin.y));
MmToPts(ptA.x - ptMin.x), MmToPts(ptA.y - ptMin.y),
MmToPts(ptB.x - ptMin.x), MmToPts(ptB.y - ptMin.y));
}
void PdfFileWriter::Triangle(STriangle *tr) {
@ -473,14 +467,12 @@ void SvgFileWriter::StartFile(void) {
// A little bit of extra space for the stroke width.
}
void SvgFileWriter::LineSegment(DWORD rgb, double w,
double x0, double y0, double x1, double y1)
{
void SvgFileWriter::LineSegment(DWORD rgb, double w, Vector ptA, Vector ptB) {
// SVG uses a coordinate system with the origin at top left, +y down
fprintf(f,
"<polyline points='%.3f,%.3f %.3f,%.3f' %s />\r\n",
(x0 - ptMin.x), (ptMax.y - y0),
(x1 - ptMin.x), (ptMax.y - y1),
(ptA.x - ptMin.x), (ptMax.y - ptA.y),
(ptB.x - ptMin.x), (ptMax.y - ptB.y),
StyleString(rgb, w));
}
@ -525,8 +517,7 @@ void SvgFileWriter::Bezier(DWORD rgb, double w, SBezier *sb) {
StyleString(rgb, w));
} else if(!sb->IsRational()) {
if(sb->deg == 1) {
LineSegment(rgb, w, sb->ctrl[0].x, sb->ctrl[0].y,
sb->ctrl[1].x, sb->ctrl[1].y);
LineSegment(rgb, w, sb->ctrl[0], sb->ctrl[1]);
} else if(sb->deg == 2) {
fprintf(f,
"<path d='M%.3f,%.3f "
@ -567,11 +558,13 @@ void HpglFileWriter::StartFile(void) {
fprintf(f, "SP1;\r\n");
}
void HpglFileWriter::LineSegment(DWORD rgb, double w,
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::LineSegment(DWORD rgb, double w, Vector ptA, Vector ptB) {
fprintf(f, "PU%d,%d;\r\n",
(int)MmToHpglUnits(ptA.x),
(int)MmToHpglUnits(ptA.y));
fprintf(f, "PD%d,%d;\r\n",
(int)MmToHpglUnits(ptB.x),
(int)MmToHpglUnits(ptB.y));
}
void HpglFileWriter::Triangle(STriangle *tr) {
@ -598,11 +591,9 @@ void Step2dFileWriter::StartFile(void) {
void Step2dFileWriter::Triangle(STriangle *tr) {
}
void Step2dFileWriter::LineSegment(DWORD rgb, double w,
double x0, double y0, double x1, double y1)
void Step2dFileWriter::LineSegment(DWORD rgb, double w, Vector ptA, Vector ptB)
{
SBezier sb = SBezier::From(Vector::From(x0, y0, 0),
Vector::From(x1, y1, 0));
SBezier sb = SBezier::From(ptA, ptB);
Bezier(rgb, w, &sb);
}

View File

@ -415,7 +415,14 @@ void SolveSpace::MenuFile(int id) {
case GraphicsWindow::MNU_EXPORT_VIEW: {
char exportFile[MAX_PATH] = "";
if(!GetSaveFile(exportFile, VEC_EXT, VEC_PATTERN)) break;
SS.ExportViewTo(exportFile);
SS.ExportViewOrWireframeTo(exportFile, false);
break;
}
case GraphicsWindow::MNU_EXPORT_WIREFRAME: {
char exportFile[MAX_PATH] = "";
if(!GetSaveFile(exportFile, V3D_EXT, V3D_PATTERN)) break;
SS.ExportViewOrWireframeTo(exportFile, true);
break;
}

View File

@ -75,15 +75,20 @@ extern char RecentFile[MAX_RECENT][MAX_PATH];
void RefreshRecentMenus(void);
int SaveFileYesNoCancel(void);
// SolveSpace native file format
#define SLVS_PATTERN "SolveSpace Models (*.slvs)\0*.slvs\0All Files (*)\0*\0\0"
#define SLVS_EXT "slvs"
// PNG format bitmap
#define PNG_PATTERN "PNG (*.png)\0*.png\0All Files (*)\0*\0\0"
#define PNG_EXT "png"
// Triangle mesh
#define STL_PATTERN "STL Mesh (*.stl)\0*.stl\0All Files (*)\0*\0\0"
#define STL_EXT "stl"
// NURBS surfaces
#define SRF_PATTERN "STEP File (*.step;*.stp)\0*.step;*.stp\0" \
"All Files(*)\0*\0\0"
#define SRF_EXT "step"
// 2d vector (lines and curves) format
#define VEC_PATTERN "PDF File (*.pdf)\0*.pdf\0" \
"Encapsulated PostScript (*.eps;*.ps)\0*.eps;*.ps\0" \
"Scalable Vector Graphics (*.svg)\0*.svg\0" \
@ -92,8 +97,15 @@ int SaveFileYesNoCancel(void);
"HPGL File (*.plt;*.hpgl)\0*.plt;*.hpgl\0" \
"All Files (*)\0*\0\0"
#define VEC_EXT "pdf"
// 3d vector (wireframe lines and curves) format
#define V3D_PATTERN "STEP File (*.step;*.stp)\0*.step;*.stp\0" \
"DXF File (*.dxf)\0*.dxf\0" \
"All Files (*)\0*\0\0"
#define V3D_EXT "step"
// Comma-separated value, like a spreadsheet would use
#define CSV_PATTERN "CSV File (*.csv)\0*.csv\0All Files (*)\0*\0\0"
#define CSV_EXT "csv"
// Native license file
#define LICENSE_PATTERN \
"License File (*.license)\0*.license\0All Files (*)\0*\0\0"
#define LICENSE_EXT "license"
@ -393,9 +405,6 @@ public:
FILE *f;
Vector ptMin, ptMax;
double width;
double r, g, b;
static double MmToPts(double mm);
static bool StringEndsIn(char *str, char *ending);
@ -408,16 +417,15 @@ public:
SBezier *sb, int depth=0);
virtual void Bezier(DWORD rgb, double width, SBezier *sb) = 0;
virtual void LineSegment(DWORD rgb, double width,
double x0, double y0, double x1, double y1) = 0;
virtual void LineSegment(DWORD rgb, double width, Vector ptA, Vector ptB)
= 0;
virtual void Triangle(STriangle *tr) = 0;
virtual void StartFile(void) = 0;
virtual void FinishAndCloseFile(void) = 0;
};
class DxfFileWriter : public VectorFileWriter {
public:
void LineSegment(DWORD rgb, double width,
double x0, double y0, double x1, double y1);
void LineSegment(DWORD rgb, double width, Vector ptA, Vector ptB);
void Triangle(STriangle *tr);
void Bezier(DWORD rgb, double width, SBezier *sb);
void StartFile(void);
@ -426,8 +434,7 @@ public:
class EpsFileWriter : public VectorFileWriter {
public:
static char *StyleString(DWORD rgb, double width);
void LineSegment(DWORD rgb, double width,
double x0, double y0, double x1, double y1);
void LineSegment(DWORD rgb, double width, Vector ptA, Vector ptB);
void Triangle(STriangle *tr);
void Bezier(DWORD rgb, double width, SBezier *sb);
void StartFile(void);
@ -439,8 +446,7 @@ public:
DWORD bodyStart;
static char *StyleString(DWORD rgb, double width);
void LineSegment(DWORD rgb, double width,
double x0, double y0, double x1, double y1);
void LineSegment(DWORD rgb, double width, Vector ptA, Vector ptB);
void Triangle(STriangle *tr);
void Bezier(DWORD rgb, double width, SBezier *sb);
void StartFile(void);
@ -449,8 +455,7 @@ public:
class SvgFileWriter : public VectorFileWriter {
public:
static char *StyleString(DWORD rgb, double width);
void LineSegment(DWORD rgb, double width,
double x0, double y0, double x1, double y1);
void LineSegment(DWORD rgb, double width, Vector ptA, Vector ptB);
void Triangle(STriangle *tr);
void Bezier(DWORD rgb, double width, SBezier *sb);
void StartFile(void);
@ -459,8 +464,7 @@ public:
class HpglFileWriter : public VectorFileWriter {
public:
static double MmToHpglUnits(double mm);
void LineSegment(DWORD rgb, double width,
double x0, double y0, double x1, double y1);
void LineSegment(DWORD rgb, double width, Vector ptA, Vector ptB);
void Triangle(STriangle *tr);
void Bezier(DWORD rgb, double width, SBezier *sb);
void StartFile(void);
@ -468,8 +472,7 @@ public:
};
class Step2dFileWriter : public VectorFileWriter {
StepFileWriter sfw;
void LineSegment(DWORD rgb, double width,
double x0, double y0, double x1, double y1);
void LineSegment(DWORD rgb, double width, Vector ptA, Vector ptB);
void Triangle(STriangle *tr);
void Bezier(DWORD rgb, double width, SBezier *sb);
void StartFile(void);
@ -626,8 +629,10 @@ public:
// And the various export options
void ExportAsPngTo(char *file);
void ExportMeshTo(char *file);
void ExportViewTo(char *file);
void ExportViewOrWireframeTo(char *file, bool wireframe);
void ExportSectionTo(char *file);
void ExportWireframeCurves(SEdgeList *sel, SBezierList *sbl,
VectorFileWriter *out);
void ExportLinesAndMesh(SEdgeList *sel, SBezierList *sbl, SMesh *sm,
Vector u, Vector v, Vector n, Vector origin,
double cameraTan,

View File

@ -105,6 +105,20 @@ SBezier SBezier::TransformedBy(Vector t, Quaternion q, bool mirror) {
return ret;
}
//-----------------------------------------------------------------------------
// Does this curve lie entirely within the specified plane? It does if all
// the control points lie in that plane.
//-----------------------------------------------------------------------------
bool SBezier::IsInPlane(Vector n, double d) {
int i;
for(i = 0; i <= deg; i++) {
if(fabs((ctrl[i]).Dot(n) - d) > LENGTH_EPS) {
return false;
}
}
return true;
}
//-----------------------------------------------------------------------------
// Is this Bezier exactly the arc of a circle, projected along the specified
// axis? If yes, return that circle's center and radius.

View File

@ -226,20 +226,20 @@ void SBezier::SplitAt(double t, SBezier *bef, SBezier *aft) {
}
}
void SBezier::MakePwlInto(SEdgeList *sel) {
void SBezier::MakePwlInto(SEdgeList *sel, double chordTol) {
List<Vector> lv;
ZERO(&lv);
MakePwlInto(&lv);
MakePwlInto(&lv, chordTol);
int i;
for(i = 1; i < lv.n; i++) {
sel->AddEdge(lv.elem[i-1], lv.elem[i]);
}
lv.Clear();
}
void SBezier::MakePwlInto(List<SCurvePt> *l) {
void SBezier::MakePwlInto(List<SCurvePt> *l, double chordTol) {
List<Vector> lv;
ZERO(&lv);
MakePwlInto(&lv);
MakePwlInto(&lv, chordTol);
int i;
for(i = 0; i < lv.n; i++) {
SCurvePt scpt;
@ -250,11 +250,17 @@ void SBezier::MakePwlInto(List<SCurvePt> *l) {
}
lv.Clear();
}
void SBezier::MakePwlInto(List<Vector> *l) {
void SBezier::MakePwlInto(List<Vector> *l, double chordTol) {
if(chordTol == 0) {
// Use the default chord tolerance.
chordTol = SS.ChordTolMm();
}
l->Add(&(ctrl[0]));
MakePwlWorker(l, 0.0, 1.0);
MakePwlWorker(l, 0.0, 1.0, chordTol);
}
void SBezier::MakePwlWorker(List<Vector> *l, double ta, double tb) {
void SBezier::MakePwlWorker(List<Vector> *l, double ta, double tb,
double chordTol)
{
Vector pa = PointAt(ta);
Vector pb = PointAt(tb);
@ -269,13 +275,13 @@ void SBezier::MakePwlWorker(List<Vector> *l, double ta, double tb) {
pm2.DistanceToLine(pa, pb.Minus(pa)));
double step = 1.0/SS.maxSegments;
if((tb - ta) < step || d < SS.ChordTolMm()) {
if((tb - ta) < step || d < chordTol) {
// A previous call has already added the beginning of our interval.
l->Add(&pb);
} else {
double tm = (ta + tb) / 2;
MakePwlWorker(l, ta, tm);
MakePwlWorker(l, tm, tb);
MakePwlWorker(l, ta, tm, chordTol);
MakePwlWorker(l, tm, tb, chordTol);
}
}

View File

@ -77,15 +77,16 @@ public:
Vector Start(void);
Vector Finish(void);
bool Equals(SBezier *b);
void MakePwlInto(SEdgeList *sel);
void MakePwlInto(List<SCurvePt> *l);
void MakePwlInto(List<Vector> *l);
void MakePwlWorker(List<Vector> *l, double ta, double tb);
void MakePwlInto(SEdgeList *sel, double chordTol=0);
void MakePwlInto(List<SCurvePt> *l, double chordTol=0);
void MakePwlInto(List<Vector> *l, double chordTol=0);
void MakePwlWorker(List<Vector> *l, double ta, double tb, double chordTol);
void AllIntersectionsWith(SBezier *sbb, SPointList *spl);
void GetBoundingProjd(Vector u, Vector orig, double *umin, double *umax);
void Reverse(void);
bool IsInPlane(Vector n, double d);
bool IsCircle(Vector axis, Vector *center, double *r);
bool IsRational(void);

View File

@ -1,7 +1,6 @@
multi-drag
copy and paste
wireframe export
interpolating splines
associative entities from solid model, as a special group