diff --git a/src/confscreen.cpp b/src/confscreen.cpp index 437f424..8f01108 100644 --- a/src/confscreen.cpp +++ b/src/confscreen.cpp @@ -26,13 +26,27 @@ void TextWindow::ScreenChangeColor(int link, uint32_t v) { } void TextWindow::ScreenChangeChordTolerance(int link, uint32_t v) { - SS.TW.ShowEditControl(3, ssprintf("%.2f", SS.chordTol)); + SS.TW.ShowEditControl(3, ssprintf("%lg", SS.chordTol)); SS.TW.edit.meaning = EDIT_CHORD_TOLERANCE; + SS.TW.edit.i = 0; } void TextWindow::ScreenChangeMaxSegments(int link, uint32_t v) { SS.TW.ShowEditControl(3, ssprintf("%d", SS.maxSegments)); SS.TW.edit.meaning = EDIT_MAX_SEGMENTS; + SS.TW.edit.i = 0; +} + +void TextWindow::ScreenChangeExportChordTolerance(int link, uint32_t v) { + SS.TW.ShowEditControl(3, ssprintf("%lg", SS.exportChordTol)); + SS.TW.edit.meaning = EDIT_CHORD_TOLERANCE; + SS.TW.edit.i = 1; +} + +void TextWindow::ScreenChangeExportMaxSegments(int link, uint32_t v) { + SS.TW.ShowEditControl(3, ssprintf("%d", SS.exportMaxSegments)); + SS.TW.edit.meaning = EDIT_MAX_SEGMENTS; + SS.TW.edit.i = 1; } void TextWindow::ScreenChangeCameraTangent(int link, uint32_t v) { @@ -182,6 +196,16 @@ void TextWindow::ShowConfiguration(void) { SS.maxSegments, &ScreenChangeMaxSegments); + Printf(false, ""); + Printf(false, "%Ft export chord tolerance (in mm)%E"); + Printf(false, "%Ba %@ %Fl%Ll%f%D[change]%E", + SS.exportChordTol, + &ScreenChangeExportChordTolerance, 0); + Printf(false, "%Ft export max piecewise linear segments%E"); + Printf(false, "%Ba %d %Fl%Ll%f[change]%E", + SS.exportMaxSegments, + &ScreenChangeExportMaxSegments); + Printf(false, ""); Printf(false, "%Ft perspective factor (0 for parallel)%E"); Printf(false, "%Ba %# %Fl%Ll%f%D[change]%E", @@ -312,13 +336,21 @@ bool TextWindow::EditControlDoneForConfiguration(const char *s) { break; } case EDIT_CHORD_TOLERANCE: { - SS.chordTol = min(10.0, max(0.1, atof(s))); - SS.GenerateAll(SolveSpaceUI::GENERATE_ALL); + if(edit.i == 0) { + SS.chordTol = max(0.0, atof(s)); + SS.GenerateAll(SolveSpaceUI::GENERATE_ALL); + } else { + SS.exportChordTol = max(0.0, atof(s)); + } break; } case EDIT_MAX_SEGMENTS: { - SS.maxSegments = min(1000, max(7, atoi(s))); - SS.GenerateAll(SolveSpaceUI::GENERATE_ALL); + if(edit.i == 0) { + SS.maxSegments = min(1000, max(7, atoi(s))); + SS.GenerateAll(SolveSpaceUI::GENERATE_ALL); + } else { + SS.exportMaxSegments = min(1000, max(7, atoi(s))); + } break; } case EDIT_CAMERA_TANGENT: { diff --git a/src/draw.cpp b/src/draw.cpp index a91e0e6..af760ef 100644 --- a/src/draw.cpp +++ b/src/draw.cpp @@ -796,28 +796,39 @@ nogrid:; // A note to indicate the origin in the just-exported file. if(SS.justExportedInfo.draw) { + Vector p, u, v; + if(SS.justExportedInfo.showOrigin) { + p = SS.justExportedInfo.pt, + u = SS.justExportedInfo.u, + v = SS.justExportedInfo.v; + } else { + p = SS.GW.offset.ScaledBy(-1); + u = SS.GW.projRight; + v = SS.GW.projUp; + } + ssglColorRGB(Style::Color(Style::DATUM)); - Vector p = SS.justExportedInfo.pt, - u = SS.justExportedInfo.u, - v = SS.justExportedInfo.v; - ssglLineWidth(1.5); - glBegin(GL_LINES); - ssglVertex3v(p.Plus(u.WithMagnitude(-15/scale))); - ssglVertex3v(p.Plus(u.WithMagnitude(30/scale))); - ssglVertex3v(p.Plus(v.WithMagnitude(-15/scale))); - ssglVertex3v(p.Plus(v.WithMagnitude(30/scale))); - glEnd(); - - ssglWriteText("(x, y) = (0, 0) for file just exported", + ssglWriteText("previewing exported geometry; press Esc to return", DEFAULT_TEXT_HEIGHT, p.Plus(u.ScaledBy(10/scale)).Plus(v.ScaledBy(10/scale)), u, v, NULL, NULL); - ssglWriteText("press Esc to clear this message", - DEFAULT_TEXT_HEIGHT, - p.Plus(u.ScaledBy(40/scale)).Plus( - v.ScaledBy(-(DEFAULT_TEXT_HEIGHT)/scale)), - u, v, NULL, NULL); + + if(SS.justExportedInfo.showOrigin) { + ssglLineWidth(1.5); + glBegin(GL_LINES); + ssglVertex3v(p.Plus(u.WithMagnitude(-15/scale))); + ssglVertex3v(p.Plus(u.WithMagnitude(30/scale))); + ssglVertex3v(p.Plus(v.WithMagnitude(-15/scale))); + ssglVertex3v(p.Plus(v.WithMagnitude(30/scale))); + glEnd(); + + ssglWriteText("(x, y) = (0, 0) for file just exported", + DEFAULT_TEXT_HEIGHT, + p.Plus(u.ScaledBy(40/scale)).Plus( + v.ScaledBy(-(DEFAULT_TEXT_HEIGHT)/scale)), + u, v, NULL, NULL); + } } // And finally the toolbar. diff --git a/src/export.cpp b/src/export.cpp index f670f1b..252a52a 100644 --- a/src/export.cpp +++ b/src/export.cpp @@ -112,6 +112,9 @@ void SolveSpaceUI::ExportViewOrWireframeTo(const std::string &filename, bool wir SEdgeList edges = {}; SBezierList beziers = {}; + SS.exportMode = true; + GenerateAll(GENERATE_ALL); + SMesh *sm = NULL; if(SS.GW.showShaded) { Group *g = SK.GetGroup(SS.GW.activeGroup); @@ -178,12 +181,16 @@ void SolveSpaceUI::ExportViewOrWireframeTo(const std::string &filename, bool wir // These file formats don't have a canvas size, so they just // get exported in the raw coordinate system. So indicate what // that was on-screen. - SS.justExportedInfo.draw = true; + SS.justExportedInfo.showOrigin = true; SS.justExportedInfo.pt = origin; SS.justExportedInfo.u = u; SS.justExportedInfo.v = v; - InvalidateGraphics(); + } else { + SS.justExportedInfo.showOrigin = false; } + + SS.justExportedInfo.draw = true; + InvalidateGraphics(); } edges.Clear(); @@ -365,7 +372,7 @@ void SolveSpaceUI::ExportLinesAndMesh(SEdgeList *sel, SBezierList *sbl, SMesh *s SEdge notClosedAt; sbl->l.ClearTags(); sblss.FindOuterFacesFrom(sbl, &spxyz, &srf, - SS.ChordTolMm()*s, + SS.ExportChordTolMm(), &allClosed, ¬ClosedAt, NULL, NULL, &leftovers); @@ -509,7 +516,7 @@ void VectorFileWriter::Output(SBezierLoopSetSet *sblss, SMesh *sm) { void VectorFileWriter::BezierAsPwl(SBezier *sb) { List lv = {}; - sb->MakePwlInto(&lv, SS.ChordTolMm() / SS.exportScale); + sb->MakePwlInto(&lv, SS.ExportChordTolMm()); int i; for(i = 1; i < lv.n; i++) { SBezier sb = SBezier::From(lv.elem[i-1], lv.elem[i]); @@ -528,7 +535,7 @@ void VectorFileWriter::BezierAsNonrationalCubic(SBezier *sb, int depth) { sb->Finish().Minus(t1.ScaledBy(1.0/3)), sb->Finish()); - double tol = SS.ChordTolMm() / SS.exportScale; + double tol = SS.ExportChordTolMm(); // Arbitrary choice, but make it a little finer than pwl tolerance since // it should be easier to achieve that with the smooth curves. tol /= 2; @@ -559,6 +566,12 @@ void VectorFileWriter::BezierAsNonrationalCubic(SBezier *sb, int depth) { // Export a triangle mesh, in the requested format. //----------------------------------------------------------------------------- void SolveSpaceUI::ExportMeshTo(const std::string &filename) { + SS.exportMode = true; + GenerateAll(GENERATE_ALL); + + Group *g = SK.GetGroup(SS.GW.activeGroup); + g->GenerateDisplayItems(); + SMesh *m = &(SK.GetGroup(SS.GW.activeGroup)->displayMesh); if(m->IsEmpty()) { Error("Active group mesh is empty; nothing to export."); @@ -585,6 +598,10 @@ void SolveSpaceUI::ExportMeshTo(const std::string &filename) { } fclose(f); + + SS.justExportedInfo.showOrigin = false; + SS.justExportedInfo.draw = true; + InvalidateGraphics(); } //----------------------------------------------------------------------------- diff --git a/src/exportstep.cpp b/src/exportstep.cpp index 47af23b..8e3936c 100644 --- a/src/exportstep.cpp +++ b/src/exportstep.cpp @@ -237,7 +237,7 @@ void StepFileWriter::ExportSurface(SSurface *ss, SBezierList *sbl) { // tolerance are required, because they are used to calculate the // contour directions and determine inner vs. outer contours. sblss.FindOuterFacesFrom(sbl, &spxyz, ss, - SS.ChordTolMm() / SS.exportScale, + SS.ExportChordTolMm(), &allClosed, ¬ClosedAt, NULL, NULL, NULL); diff --git a/src/graphicswin.cpp b/src/graphicswin.cpp index 980d1be..c8c4e21 100644 --- a/src/graphicswin.cpp +++ b/src/graphicswin.cpp @@ -732,6 +732,10 @@ void GraphicsWindow::MenuEdit(int id) { for(p = SK.param.First(); p; p = SK.param.NextAfter(p)) { p->free = false; } + if(SS.exportMode) { + SS.exportMode = false; + SS.GenerateAll(SolveSpaceUI::GENERATE_ALL); + } break; case MNU_SELECT_ALL: { diff --git a/src/solvespace.cpp b/src/solvespace.cpp index 609c474..d9c343f 100644 --- a/src/solvespace.cpp +++ b/src/solvespace.cpp @@ -36,10 +36,16 @@ void SolveSpaceUI::Init() { lightDir[1].x = CnfThawFloat( 1.0f, "LightDir_1_Right" ); lightDir[1].y = CnfThawFloat( 0.0f, "LightDir_1_Up" ); lightDir[1].z = CnfThawFloat( 0.0f, "LightDir_1_Forward" ); + + exportMode = false; // Chord tolerance chordTol = CnfThawFloat(2.0f, "ChordTolerance"); // Max pwl segments to generate maxSegments = CnfThawInt(10, "MaxSegments"); + // Chord tolerance + exportChordTol = CnfThawFloat(0.1f, "ExportChordTolerance"); + // Max pwl segments to generate + exportMaxSegments = CnfThawInt(64, "ExportMaxSegments"); // View units viewUnits = (Unit)CnfThawInt((uint32_t)UNIT_MM, "ViewUnits"); // Number of digits after the decimal point @@ -157,6 +163,10 @@ void SolveSpaceUI::Exit(void) { CnfFreezeFloat((float)chordTol, "ChordTolerance"); // Max pwl segments to generate CnfFreezeInt((uint32_t)maxSegments, "MaxSegments"); + // Export Chord tolerance + CnfFreezeFloat((float)exportChordTol, "ExportChordTolerance"); + // Export Max pwl segments to generate + CnfFreezeInt((uint32_t)exportMaxSegments, "ExportMaxSegments"); // View units CnfFreezeInt((uint32_t)viewUnits, "ViewUnits"); // Number of digits after the decimal point @@ -259,7 +269,15 @@ double SolveSpaceUI::StringToMm(const std::string &str) { return std::stod(str) * MmPerUnit(); } double SolveSpaceUI::ChordTolMm(void) { - return SS.chordTol / SS.GW.scale; + if(exportMode) return ExportChordTolMm(); + return chordTol / GW.scale; +} +double SolveSpaceUI::ExportChordTolMm(void) { + return exportChordTol / exportScale; +} +int SolveSpaceUI::GetMaxSegments(void) { + if(exportMode) return exportMaxSegments; + return maxSegments; } int SolveSpaceUI::UnitDigitsAfterDecimal(void) { return (viewUnits == UNIT_INCHES) ? afterDecimalInch : afterDecimalMm; diff --git a/src/solvespace.h b/src/solvespace.h index fb91055..1be244a 100644 --- a/src/solvespace.h +++ b/src/solvespace.h @@ -752,6 +752,8 @@ public: double ambientIntensity; double chordTol; int maxSegments; + double exportChordTol; + int exportMaxSegments; double cameraTangent; float gridSpacing; float exportScale; @@ -764,6 +766,7 @@ public: bool exportShadedTriangles; bool exportPwlCurves; bool exportCanvasSizeAuto; + bool exportMode; struct { float left; float right; @@ -800,6 +803,8 @@ public: int UnitDigitsAfterDecimal(void); void SetUnitDigitsAfterDecimal(int v); double ChordTolMm(void); + double ExportChordTolMm(void); + int GetMaxSegments(void); bool usePerspectiveProj; double CameraTangent(void); @@ -892,7 +897,7 @@ public: Vector origin; } bgImage; struct { - bool draw; + bool draw, showOrigin; Vector pt, u, v; } justExportedInfo; diff --git a/src/srf/ratpoly.cpp b/src/srf/ratpoly.cpp index b80056b..2f6a0bb 100644 --- a/src/srf/ratpoly.cpp +++ b/src/srf/ratpoly.cpp @@ -281,7 +281,7 @@ void SBezier::MakePwlWorker(List *l, double ta, double tb, double chordT Vector pm = PointAt((ta + tb) / 2.0); double d = pm.DistanceToLine(pa, pb.Minus(pa)); - double step = 1.0/SS.maxSegments; + double step = 1.0/SS.GetMaxSegments(); if((tb - ta) < step || d < chordTol) { // A previous call has already added the beginning of our interval. l->Add(&pb); @@ -311,7 +311,7 @@ void SBezier::MakePwlInitialWorker(List *l, double ta, double tb, double pm3.DistanceToLine(pa, dir) }); - double step = 1.0/SS.maxSegments; + double step = 1.0/SS.GetMaxSegments(); if((tb - ta) < step || d < chordTol) { // A previous call has already added the beginning of our interval. l->Add(&pb); diff --git a/src/srf/surfinter.cpp b/src/srf/surfinter.cpp index a8c33aa..2959393 100644 --- a/src/srf/surfinter.cpp +++ b/src/srf/surfinter.cpp @@ -376,7 +376,7 @@ void SSurface::IntersectAgainst(SSurface *b, SShell *agnstA, SShell *agnstB, // Our chord tolerance is whatever the user specified double maxtol = SS.ChordTolMm(); - int maxsteps = max(300, SS.maxSegments*3); + int maxsteps = max(300, SS.GetMaxSegments()*3); // The curve starts at our starting point. SCurvePt padd = {}; diff --git a/src/srf/triangulate.cpp b/src/srf/triangulate.cpp index f4962ce..816eb3e 100644 --- a/src/srf/triangulate.cpp +++ b/src/srf/triangulate.cpp @@ -409,7 +409,7 @@ void SSurface::MakeTriangulationGridInto(List *l, double vs, double vf, worst = max(worst, pm2.DistanceToLine(ps, pf.Minus(ps))); } - double step = 1.0/SS.maxSegments; + double step = 1.0/SS.GetMaxSegments(); if((vf - vs) < step || worst < SS.ChordTolMm()) { l->Add(&vf); } else { diff --git a/src/ui.h b/src/ui.h index 7ea3947..90da798 100644 --- a/src/ui.h +++ b/src/ui.h @@ -299,6 +299,8 @@ public: static void ScreenChangeColor(int link, uint32_t v); static void ScreenChangeChordTolerance(int link, uint32_t v); static void ScreenChangeMaxSegments(int link, uint32_t v); + static void ScreenChangeExportChordTolerance(int link, uint32_t v); + static void ScreenChangeExportMaxSegments(int link, uint32_t v); static void ScreenChangeCameraTangent(int link, uint32_t v); static void ScreenChangeGridSpacing(int link, uint32_t v); static void ScreenChangeDigitsAfterDecimal(int link, uint32_t v);