From 83bbc004dc9c32eaa0a204a3346c7ce21181c5c9 Mon Sep 17 00:00:00 2001 From: Jonathan Westhues Date: Mon, 12 Oct 2009 02:40:48 -0800 Subject: [PATCH] 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] --- export.cpp | 48 ++++++++++++++++++++++++++++++------------ exportvector.cpp | 55 ++++++++++++++++++++---------------------------- solvespace.cpp | 9 +++++++- solvespace.h | 41 ++++++++++++++++++++---------------- srf/curve.cpp | 14 ++++++++++++ srf/ratpoly.cpp | 26 ++++++++++++++--------- srf/surface.h | 9 ++++---- wishlist.txt | 1 - 8 files changed, 123 insertions(+), 80 deletions(-) diff --git a/export.cpp b/export.cpp index 8a872b3..38b8d09 100644 --- a/export.cpp +++ b/export.cpp @@ -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,21 +158,42 @@ void SolveSpace::ExportViewTo(char *filename) { } } - Vector u = SS.GW.projRight, - v = SS.GW.projUp, - n = u.Cross(v), - origin = SS.GW.offset.ScaledBy(-1); + 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), + origin = SS.GW.offset.ScaledBy(-1); - VectorFileWriter *out = VectorFileWriter::ForFile(filename); - if(out) { - ExportLinesAndMesh(&edges, &beziers, sm, - u, v, n, origin, SS.CameraTangent()*SS.GW.scale, - out); + VectorFileWriter *out = VectorFileWriter::ForFile(filename); + if(out) { + ExportLinesAndMesh(&edges, &beziers, sm, + 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 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(); } diff --git a/exportvector.cpp b/exportvector.cpp index fe6632c..bcb3f7f 100644 --- a/exportvector.cpp +++ b/exportvector.cpp @@ -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, "\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, " 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 *l) { +void SBezier::MakePwlInto(List *l, double chordTol) { List 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 *l) { } lv.Clear(); } -void SBezier::MakePwlInto(List *l) { +void SBezier::MakePwlInto(List *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 *l, double ta, double tb) { +void SBezier::MakePwlWorker(List *l, double ta, double tb, + double chordTol) +{ Vector pa = PointAt(ta); Vector pb = PointAt(tb); @@ -269,13 +275,13 @@ void SBezier::MakePwlWorker(List *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); } } diff --git a/srf/surface.h b/srf/surface.h index a0cabad..df3c841 100644 --- a/srf/surface.h +++ b/srf/surface.h @@ -77,15 +77,16 @@ public: Vector Start(void); Vector Finish(void); bool Equals(SBezier *b); - void MakePwlInto(SEdgeList *sel); - void MakePwlInto(List *l); - void MakePwlInto(List *l); - void MakePwlWorker(List *l, double ta, double tb); + void MakePwlInto(SEdgeList *sel, double chordTol=0); + void MakePwlInto(List *l, double chordTol=0); + void MakePwlInto(List *l, double chordTol=0); + void MakePwlWorker(List *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); diff --git a/wishlist.txt b/wishlist.txt index 0a30abd..04cca80 100644 --- a/wishlist.txt +++ b/wishlist.txt @@ -1,7 +1,6 @@ multi-drag copy and paste -wireframe export interpolating splines associative entities from solid model, as a special group