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]
This commit is contained in:
Jonathan Westhues 2008-02-07 01:53:52 -08:00
parent 115dbce61b
commit 7b7d2f92e9
11 changed files with 121 additions and 8 deletions

View File

@ -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.
<h3>Measure Volume</h3>
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.
<h2>Export</h2>

View File

@ -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) },

View File

@ -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.

View File

@ -538,12 +538,15 @@ void Group::GenerateMesh(void) {
}
done:
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;
}
}
SMesh *Group::PreviousGroupMesh(void) {

View File

@ -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;

View File

@ -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) {

View File

@ -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.
//-----------------------------------------------------------------------------

View File

@ -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, "");

4
ui.h
View File

@ -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);

View File

@ -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));

View File

@ -1,8 +1,6 @@
get rid of the oops() calls in the mesh codes
smooth shading
leak fixing
cylindrical faces?
long term