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