From 89eb208660640363b74245c430f69fd841f141a2 Mon Sep 17 00:00:00 2001 From: EvilSpirit Date: Wed, 27 Jan 2016 10:07:54 +0600 Subject: [PATCH] Use a separate value of chord tolerance for exporting. Before this commit, a single chord tolerance was used for both displaying and exporting geometry. Moreover, this chord tolerance was specified in screen pixels, and as such depended on zoom level. This was inconvenient: exporting geometry with a required level of precision required awkward manipulations of viewport. Moreover, since some operations, e.g. mesh watertightness checking, were done on triangle meshes which are generated differently depending on the zoom level, these operations could report wildly different and quite confusing results depending on zoom level. The chord tolerance for display and export pursue completely distinct goals: display chord tolerance should be set high enough to achieve both fast regeneration and legible rendering, whereas export chord tolerance should be set to match the dimension tolerance of the fabrication process. This commit introduces two distinct chord tolerances: a display and an export one. Both chord tolerances are absolute and expressed in millimeters; this is inappropriate for display purposes but will be fixed in the next commits. After exporting, the geometry is redrawn with the chord tolerance configured for the export and an overlay message is displayed; pressing Esc clears the message and returns the display back to normal. --- src/confscreen.cpp | 42 +++++++++++++++++++++++++++++++++----- src/draw.cpp | 45 +++++++++++++++++++++++++---------------- src/export.cpp | 27 ++++++++++++++++++++----- src/exportstep.cpp | 2 +- src/graphicswin.cpp | 4 ++++ src/solvespace.cpp | 20 +++++++++++++++++- src/solvespace.h | 7 ++++++- src/srf/ratpoly.cpp | 4 ++-- src/srf/surfinter.cpp | 2 +- src/srf/triangulate.cpp | 2 +- src/ui.h | 2 ++ 11 files changed, 123 insertions(+), 34 deletions(-) diff --git a/src/confscreen.cpp b/src/confscreen.cpp index 437f4248..8f01108a 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 a91e0e6d..af760efb 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 f670f1b2..252a52a3 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 47af23bc..8e3936c3 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 980d1be5..c8c4e217 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 609c4741..d9c343fb 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 fb910557..1be244ac 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 b80056b4..2f6a0bb7 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 a8c33aaa..29593931 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 f4962ce8..816eb3ea 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 7ea39473..90da7988 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);