From 7b7d2f92e92130a4039a1ffc206dd35fc534d720 Mon Sep 17 00:00:00 2001 From: Jonathan Westhues Date: Thu, 7 Feb 2008 01:53:52 -0800 Subject: [PATCH] Add code to evaluate the volume of a mesh, thanks to the divergence theorem; it's evaluated as a surface integral over each triangle. And don't regenerate the emphasized edges unless we have to; specifically, don't do it when the only dirty group is the drawing group. [git-p4: depot-paths = "//depot/solvespace/": change = 1849] --- doc/reference.txt | 12 ++++++++++ file.cpp | 1 + generate.cpp | 15 ++++++++++++ groupmesh.cpp | 13 ++++++---- sketch.h | 3 +++ solvespace.cpp | 60 ++++++++++++++++++++++++++++++++++++++++++++++- textscreens.cpp | 17 ++++++++++++++ textwin.cpp | 1 + ui.h | 4 ++++ undoredo.cpp | 1 + wishlist.txt | 2 -- 11 files changed, 121 insertions(+), 8 deletions(-) diff --git a/doc/reference.txt b/doc/reference.txt index 10a1fbd..9f8db15 100644 --- a/doc/reference.txt +++ b/doc/reference.txt @@ -862,6 +862,18 @@ for new users; to learn about this program, see the video tutorials. this case, it may be useful to try stepping the dimension to its new value, instead of changing it in a single step. +

Measure Volume

+ + This feature reports the volume of the part. Depending on + the active units, the volume is given in cubic inches, or in + millilitres and cubic millimeters. + + If the part contains smooth curves (e.g. circles), then the + mesh is not an exact representation of the geometry. This means + that the measured volume is only an approximation; for a mesh + that looks fairly smooth on-screen, expect an error around one + percent. To decrease this error, choose a finer chord tolerance. +

Export

diff --git a/file.cpp b/file.cpp index e23d392..c61c44f 100644 --- a/file.cpp +++ b/file.cpp @@ -62,6 +62,7 @@ void SolveSpace::NewFile(void) { const SolveSpace::SaveTable SolveSpace::SAVED[] = { { 'g', "Group.h.v", 'x', &(SS.sv.g.h.v) }, { 'g', "Group.type", 'd', &(SS.sv.g.type) }, + { 'g', "Group.order", 'd', &(SS.sv.g.order) }, { 'g', "Group.name", 'N', &(SS.sv.g.name) }, { 'g', "Group.activeWorkplane.v", 'x', &(SS.sv.g.activeWorkplane.v) }, { 'g', "Group.opA.v", 'x', &(SS.sv.g.opA.v) }, diff --git a/generate.cpp b/generate.cpp index 1a77aac..20d6b0f 100644 --- a/generate.cpp +++ b/generate.cpp @@ -127,16 +127,31 @@ bool SolveSpace::PruneConstraints(hGroup hg) { void SolveSpace::GenerateAll(void) { int i; int firstDirty = INT_MAX, lastVisible = 0; + bool markVvMeshDirty = false; // Start from the first dirty group, and solve until the active group, // since all groups after the active group are hidden. for(i = 0; i < group.n; i++) { Group *g = &(group.elem[i]); + g->order = i; if((!g->clean) || (g->solved.how != Group::SOLVED_OKAY)) { firstDirty = min(firstDirty, i); + markVvMeshDirty = true; } if(g->h.v == SS.GW.activeGroup.v) { lastVisible = i; } + if(markVvMeshDirty) { + if(firstDirty == i && + (g->type == Group::DRAWING_3D || + g->type == Group::DRAWING_WORKPLANE)) + { + // These groups don't change the mesh, so there's no need + // to regenerate the vertex-to-vertex mesh if they're the + // first dirty one. + } else { + g->vvMeshClean = false; + } + } } if(firstDirty == INT_MAX || lastVisible == 0) { // All clean; so just regenerate the entities, and don't solve anything. diff --git a/groupmesh.cpp b/groupmesh.cpp index d4dc3aa..00547a1 100644 --- a/groupmesh.cpp +++ b/groupmesh.cpp @@ -538,11 +538,14 @@ void Group::GenerateMesh(void) { } done: - emphEdges.Clear(); - if(h.v == SS.GW.activeGroup.v && SS.edgeColor != 0) { - SKdNode *root = SKdNode::From(&runningMesh); - root->SnapToMesh(&runningMesh); - root->MakeCertainEdgesInto(&emphEdges, true); + if(!vvMeshClean) { + emphEdges.Clear(); + if(h.v == SS.GW.activeGroup.v && SS.edgeColor != 0) { + SKdNode *root = SKdNode::From(&runningMesh); + root->SnapToMesh(&runningMesh); + root->MakeCertainEdgesInto(&emphEdges, true); + } + vvMeshClean = true; } } diff --git a/sketch.h b/sketch.h index e568182..4174ff6 100644 --- a/sketch.h +++ b/sketch.h @@ -89,10 +89,13 @@ public: static const int IMPORTED = 5300; int type; + int order; + hGroup opA; hGroup opB; bool visible; bool clean; + bool vvMeshClean; hEntity activeWorkplane; double valA; double valB; diff --git a/solvespace.cpp b/solvespace.cpp index a0e2a7c..a27ff9e 100644 --- a/solvespace.cpp +++ b/solvespace.cpp @@ -358,8 +358,66 @@ void SolveSpace::MenuAnalyze(int id) { } break; - case GraphicsWindow::MNU_VOLUME: + case GraphicsWindow::MNU_VOLUME: { + SMesh *m = &(SS.GetGroup(SS.GW.activeGroup)->runningMesh); + + double vol = 0; + int i; + for(i = 0; i < m->l.n; i++) { + STriangle tr = m->l.elem[i]; + + // Translate to place vertex A at (x, y, 0) + Vector trans = Vector::From(tr.a.x, tr.a.y, 0); + tr.a = (tr.a).Minus(trans); + tr.b = (tr.b).Minus(trans); + tr.c = (tr.c).Minus(trans); + + // Rotate to place vertex B on the y-axis. Depending on + // whether the triangle is CW or CCW, C is either to the + // right or to the left of the y-axis. This handles the + // sign of our normal. + Vector u = Vector::From(-tr.b.y, tr.b.x, 0); + u = u.WithMagnitude(1); + Vector v = Vector::From(tr.b.x, tr.b.y, 0); + v = v.WithMagnitude(1); + Vector n = Vector::From(0, 0, 1); + + tr.a = (tr.a).DotInToCsys(u, v, n); + tr.b = (tr.b).DotInToCsys(u, v, n); + tr.c = (tr.c).DotInToCsys(u, v, n); + + n = tr.Normal().WithMagnitude(1); + + // Triangles on edge don't contribute + if(fabs(n.z) < LENGTH_EPS) continue; + + // The plane has equation p dot n = a dot n + double d = (tr.a).Dot(n); + // nx*x + ny*y + nz*z = d + // nz*z = d - nx*x - ny*y + double A = -n.x/n.z, B = -n.y/n.z, C = d/n.z; + + double mac = tr.c.y/tr.c.x, mbc = (tr.c.y - tr.b.y)/tr.c.x; + double xc = tr.c.x, yb = tr.b.y; + + // I asked Maple for + // int(int(A*x + B*y +C, y=mac*x..(mbc*x + yb)), x=0..xc); + double integral = + (1.0/3)*( + A*(mbc-mac)+ + (1.0/2)*B*(mbc*mbc-mac*mac) + )*(xc*xc*xc)+ + (1.0/2)*(A*yb+B*yb*mbc+C*(mbc-mac))*xc*xc+ + C*yb*xc+ + (1.0/2)*B*yb*yb*xc; + + vol += integral; + } + SS.TW.shown.volume = vol; + SS.TW.GoToScreen(TextWindow::SCREEN_MESH_VOLUME); + SS.later.showTW = true; break; + } case GraphicsWindow::MNU_TRACE_PT: if(gs.points == 1 && gs.n == 1) { diff --git a/textscreens.cpp b/textscreens.cpp index fc30f12..4913d28 100644 --- a/textscreens.cpp +++ b/textscreens.cpp @@ -674,6 +674,23 @@ void TextWindow::ShowStepDimension(void) { Printf(true, "(or %Fl%Ll%fcancel operation%E)", &ScreenHome); } +//----------------------------------------------------------------------------- +// A report of the volume of the mesh. No interaction, output-only. +//----------------------------------------------------------------------------- +void TextWindow::ShowMeshVolume(void) { + Printf(true, "%FtMESH VOLUME"); + + if(SS.viewUnits == SolveSpace::UNIT_INCHES) { + Printf(true, " %3 in^3", shown.volume/(25.4*25.4*25.4)); + } else { + Printf(true, " %2 mm^3", shown.volume); + Printf(false, " %2 mL", shown.volume/(10*10*10)); + } + + Printf(true, "%Fl%Ll%f(back)%E", &ScreenHome); +} + + //----------------------------------------------------------------------------- // The edit control is visible, and the user just pressed enter. //----------------------------------------------------------------------------- diff --git a/textwin.cpp b/textwin.cpp index 6f995b0..a7676ba 100644 --- a/textwin.cpp +++ b/textwin.cpp @@ -222,6 +222,7 @@ void TextWindow::Show(void) { case SCREEN_GROUP_SOLVE_INFO: ShowGroupSolveInfo(); break; case SCREEN_CONFIGURATION: ShowConfiguration(); break; case SCREEN_STEP_DIMENSION: ShowStepDimension(); break; + case SCREEN_MESH_VOLUME: ShowMeshVolume(); break; } } Printf(false, ""); diff --git a/ui.h b/ui.h index 2ba0d7a..9241141 100644 --- a/ui.h +++ b/ui.h @@ -47,6 +47,7 @@ public: static const int SCREEN_GROUP_SOLVE_INFO = 2; static const int SCREEN_CONFIGURATION = 3; static const int SCREEN_STEP_DIMENSION = 4; + static const int SCREEN_MESH_VOLUME = 5; typedef struct { int screen; @@ -56,6 +57,8 @@ public: bool dimIsDistance; double dimFinish; int dimSteps; + + double volume; } ShownState; ShownState shown; @@ -94,6 +97,7 @@ public: void ShowGroupSolveInfo(void); void ShowConfiguration(void); void ShowStepDimension(void); + void ShowMeshVolume(void); // Special screen, based on selection void DescribeSelection(void); diff --git a/undoredo.cpp b/undoredo.cpp index 45b72d2..00928cc 100644 --- a/undoredo.cpp +++ b/undoredo.cpp @@ -46,6 +46,7 @@ void SolveSpace::PushFromCurrentOnto(UndoStack *uk) { // And then clean up all the stuff that needs to be a deep copy, // and zero out all the dynamic stuff that will get regenerated. dest.clean = false; + dest.vvMeshClean = false; ZERO(&(dest.solved)); ZERO(&(dest.poly)); ZERO(&(dest.polyError)); diff --git a/wishlist.txt b/wishlist.txt index 693eda7..446fd7f 100644 --- a/wishlist.txt +++ b/wishlist.txt @@ -1,8 +1,6 @@ get rid of the oops() calls in the mesh codes -smooth shading leak fixing -cylindrical faces? long term