Add beginnings of stuff to represent surfaces as either meshes or

exact surface shells. And add interference checking; I'll be lazy
and just do that on the meshes, by modifying the self-intersection
tester to ignore coplanar triangles (since that can happen in an
assembly).

[git-p4: depot-paths = "//depot/solvespace/": change = 1958]
This commit is contained in:
Jonathan Westhues 2009-05-22 02:02:02 -08:00
parent ddf9364257
commit 03ecbad981
7 changed files with 141 additions and 61 deletions

View File

@ -59,7 +59,7 @@ void Group::GenerateShellForStepAndRepeat(void) {
ZERO(&workA); ZERO(&workA);
ZERO(&workB); ZERO(&workB);
SShell *soFar = &workA, *scratch = &workB; SShell *soFar = &workA, *scratch = &workB;
soFar->MakeFromCopyOf(src->PreviousGroupShell()); soFar->MakeFromCopyOf(&(src->PreviousGroup()->runningShell));
int n = (int)valA, a0 = 0; int n = (int)valA, a0 = 0;
if(subtype == ONE_SIDED && skipFirst) { if(subtype == ONE_SIDED && skipFirst) {
@ -234,13 +234,13 @@ void Group::GenerateShellAndMesh(void) {
// same as last time, no combining required. Likewise if we have a mesh // same as last time, no combining required. Likewise if we have a mesh
// but it's suppressed. // but it's suppressed.
if(suppress) { if(suppress) {
runningShell.MakeFromCopyOf(PreviousGroupShell()); runningShell.MakeFromCopyOf(&(PreviousGroup()->runningShell));
goto done; goto done;
} }
// So our group's shell appears in thisShell. Combine this with the // So our group's shell appears in thisShell. Combine this with the
// previous group's shell, using the requested operation. // previous group's shell, using the requested operation.
SShell *a = PreviousGroupShell(); SShell *a = &(PreviousGroup()->runningShell);
if(meshCombine == COMBINE_AS_UNION) { if(meshCombine == COMBINE_AS_UNION) {
runningShell.MakeFromUnionOf(a, &thisShell); runningShell.MakeFromUnionOf(a, &thisShell);
} else if(meshCombine == COMBINE_AS_DIFFERENCE) { } else if(meshCombine == COMBINE_AS_DIFFERENCE) {
@ -263,14 +263,14 @@ void Group::GenerateDisplayItems(void) {
} }
} }
SShell *Group::PreviousGroupShell(void) { Group *Group::PreviousGroup(void) {
int i; int i;
for(i = 0; i < SK.group.n; i++) { for(i = 0; i < SK.group.n; i++) {
Group *g = &(SK.group.elem[i]); Group *g = &(SK.group.elem[i]);
if(g->h.v == h.v) break; if(g->h.v == h.v) break;
} }
if(i == 0 || i >= SK.group.n) oops(); if(i == 0 || i >= SK.group.n) oops();
return &(SK.group.elem[i-1].runningShell); return &(SK.group.elem[i-1]);
} }
void Group::Draw(void) { void Group::Draw(void) {

127
mesh.cpp
View File

@ -477,7 +477,7 @@ void SKdNode::MakeMeshInto(SMesh *m) {
} }
void SKdNode::FindEdgeOn(Vector a, Vector b, int *n, int cnt, void SKdNode::FindEdgeOn(Vector a, Vector b, int *n, int cnt,
bool *inter, bool *fwd) bool coplanarIsInter, bool *inter, bool *fwd)
{ {
if(gt && lt) { if(gt && lt) {
double ac = a.Element(which), double ac = a.Element(which),
@ -485,61 +485,86 @@ void SKdNode::FindEdgeOn(Vector a, Vector b, int *n, int cnt,
if(ac < c + KDTREE_EPS || if(ac < c + KDTREE_EPS ||
bc < c + KDTREE_EPS) bc < c + KDTREE_EPS)
{ {
lt->FindEdgeOn(a, b, n, cnt, inter, fwd); lt->FindEdgeOn(a, b, n, cnt, coplanarIsInter, inter, fwd);
} }
if(ac > c - KDTREE_EPS || if(ac > c - KDTREE_EPS ||
bc > c - KDTREE_EPS) bc > c - KDTREE_EPS)
{ {
gt->FindEdgeOn(a, b, n, cnt, inter, fwd); gt->FindEdgeOn(a, b, n, cnt, coplanarIsInter, inter, fwd);
} }
} else { return;
STriangleLl *ll; }
for(ll = tris; ll; ll = ll->next) {
STriangle *tr = ll->tri;
if(tr->tag == cnt) continue; // We are a leaf node; so we iterate over all the triangles in our
// linked list.
STriangleLl *ll;
for(ll = tris; ll; ll = ll->next) {
STriangle *tr = ll->tri;
// Test if this triangle matches up with the given edge if(tr->tag == cnt) continue;
if((a.Equals(tr->b) && b.Equals(tr->a)) ||
(a.Equals(tr->c) && b.Equals(tr->b)) || // Test if this triangle matches up with the given edge
(a.Equals(tr->a) && b.Equals(tr->c))) if((a.Equals(tr->b) && b.Equals(tr->a)) ||
{ (a.Equals(tr->c) && b.Equals(tr->b)) ||
(*n)++; (a.Equals(tr->a) && b.Equals(tr->c)))
// Record whether this triangle is front- or back-facing. {
if(tr->Normal().z > LENGTH_EPS) { (*n)++;
*fwd = true; // Record whether this triangle is front- or back-facing.
} else { if(tr->Normal().z > LENGTH_EPS) {
*fwd = false; *fwd = true;
}
} else if(((a.Equals(tr->a) && b.Equals(tr->b)) ||
(a.Equals(tr->b) && b.Equals(tr->c)) ||
(a.Equals(tr->c) && b.Equals(tr->a))))
{
// It's an edge of this triangle, okay.
} else { } else {
// Check for self-intersection *fwd = false;
Vector n = (tr->Normal()).WithMagnitude(1); }
double d = (tr->a).Dot(n); } else if(((a.Equals(tr->a) && b.Equals(tr->b)) ||
double pa = a.Dot(n) - d, pb = b.Dot(n) - d; (a.Equals(tr->b) && b.Equals(tr->c)) ||
// It's an intersection if neither point lies in-plane, (a.Equals(tr->c) && b.Equals(tr->a))))
// and the edge crosses the plane (should handle in-plane {
// intersections separately but don't yet). // It's an edge of this triangle, okay.
if((pa < -LENGTH_EPS || pa > LENGTH_EPS) && } else {
(pb < -LENGTH_EPS || pb > LENGTH_EPS) && // Check for self-intersection
(pa*pb < 0)) Vector n = (tr->Normal()).WithMagnitude(1);
{ double d = (tr->a).Dot(n);
// The edge crosses the plane of the triangle; now see if double pa = a.Dot(n) - d, pb = b.Dot(n) - d;
// it crosses inside the triangle. // It's an intersection if neither point lies in-plane,
if(tr->ContainsPointProjd(b.Minus(a), a)) { // and the edge crosses the plane (should handle in-plane
// intersections separately but don't yet).
if((pa < -LENGTH_EPS || pa > LENGTH_EPS) &&
(pb < -LENGTH_EPS || pb > LENGTH_EPS) &&
(pa*pb < 0))
{
// The edge crosses the plane of the triangle; now see if
// it crosses inside the triangle.
if(tr->ContainsPointProjd(b.Minus(a), a)) {
if(coplanarIsInter) {
*inter = true; *inter = true;
} else {
Vector p = Vector::AtIntersectionOfPlaneAndLine(
n, d, a, b, NULL);
Vector ta = tr->a,
tb = tr->b,
tc = tr->c;
if((p.DistanceToLine(ta, tb.Minus(ta)) < LENGTH_EPS) ||
(p.DistanceToLine(tb, tc.Minus(tb)) < LENGTH_EPS) ||
(p.DistanceToLine(tc, ta.Minus(tc)) < LENGTH_EPS))
{
// Intersection lies on edge. This happens when
// our edge is from a triangle coplanar with
// another triangle in the mesh. We don't test
// the edge against triangles whose plane contains
// that edge, but we do end up testing against
// the coplanar triangle's neighbours, which we
// will intersect on their edges.
} else {
*inter = true;
}
} }
} }
} }
// Ensure that we don't count this triangle twice if it appears
// in two buckets of the kd tree.
tr->tag = cnt;
} }
// Ensure that we don't count this triangle twice if it appears
// in two buckets of the kd tree.
tr->tag = cnt;
} }
} }
@ -679,7 +704,17 @@ void SKdNode::OcclusionTestLine(SEdge orig, SEdgeList *sel, int cnt) {
} }
} }
void SKdNode::MakeNakedEdgesInto(SEdgeList *sel, bool *inter, bool *leaky) { //-----------------------------------------------------------------------------
// Report all naked edges of the mesh (i.e., edges that don't join up to
// a single anti-parallel edge of another triangle), and all edges that
// intersect another triangle. If coplanarIsInter, then edges coplanar with
// another triangle and within it are reported, otherwise not. We report
// in *inter and *leaky whether the mesh is self-intersecting or leaky
// (having naked edges) respectively.
//-----------------------------------------------------------------------------
void SKdNode::MakeNakedEdgesInto(SEdgeList *sel, bool coplanarIsInter,
bool *inter, bool *leaky)
{
if(inter) *inter = false; if(inter) *inter = false;
if(leaky) *leaky = false; if(leaky) *leaky = false;
@ -699,7 +734,7 @@ void SKdNode::MakeNakedEdgesInto(SEdgeList *sel, bool *inter, bool *leaky) {
int n = 0, nOther = 0; int n = 0, nOther = 0;
bool thisIntersects = false, fwd; bool thisIntersects = false, fwd;
FindEdgeOn(a, b, &n, cnt, &thisIntersects, &fwd); FindEdgeOn(a, b, &n, cnt, coplanarIsInter, &thisIntersects, &fwd);
if(n != 1) { if(n != 1) {
sel->AddEdge(a, b); sel->AddEdge(a, b);
if(leaky) *leaky = true; if(leaky) *leaky = true;
@ -735,7 +770,7 @@ void SKdNode::MakeTurningEdgesInto(SEdgeList *sel) {
int n = 0; int n = 0;
bool inter, fwd; bool inter, fwd;
FindEdgeOn(a, b, &n, cnt, &inter, &fwd); FindEdgeOn(a, b, &n, cnt, true, &inter, &fwd);
if(n == 1) { if(n == 1) {
// and its neighbour is front-facing, so generate the edge. // and its neighbour is front-facing, so generate the edge.
if(fwd) sel->AddEdge(a, b); if(fwd) sel->AddEdge(a, b);

View File

@ -224,9 +224,10 @@ public:
void MakeMeshInto(SMesh *m); void MakeMeshInto(SMesh *m);
void ClearTags(void); void ClearTags(void);
void FindEdgeOn(Vector a, Vector b, int *n, int cnt, void FindEdgeOn(Vector a, Vector b, int *n, int cnt, bool coplanarIsInter,
bool *inter, bool *fwd); bool *inter, bool *fwd);
void MakeNakedEdgesInto(SEdgeList *sel, bool *inter=NULL, bool *leaky=NULL); void MakeNakedEdgesInto(SEdgeList *sel, bool coplanarIsInter,
bool *inter, bool *leaky);
void MakeTurningEdgesInto(SEdgeList *sel); void MakeTurningEdgesInto(SEdgeList *sel);
void OcclusionTestLine(SEdge orig, SEdgeList *sel, int cnt); void OcclusionTestLine(SEdge orig, SEdgeList *sel, int cnt);

View File

@ -207,7 +207,7 @@ public:
bool AssembleLoops(void); bool AssembleLoops(void);
void GenerateLoops(void); void GenerateLoops(void);
// And the mesh stuff // And the mesh stuff
SShell *PreviousGroupShell(void); Group *PreviousGroup(void);
void GenerateShellForStepAndRepeat(void); void GenerateShellForStepAndRepeat(void);
void GenerateDisplayItems(void); void GenerateDisplayItems(void);
void GenerateShellAndMesh(void); void GenerateShellAndMesh(void);

View File

@ -420,7 +420,7 @@ void SolveSpace::MenuAnalyze(int id) {
SMesh *m = &(SK.GetGroup(SS.GW.activeGroup)->displayMesh); SMesh *m = &(SK.GetGroup(SS.GW.activeGroup)->displayMesh);
SKdNode *root = SKdNode::From(m); SKdNode *root = SKdNode::From(m);
bool inters, leaks; bool inters, leaks;
root->MakeNakedEdgesInto(&(SS.nakedEdges), &inters, &leaks); root->MakeNakedEdgesInto(&(SS.nakedEdges), true, &inters, &leaks);
InvalidateGraphics(); InvalidateGraphics();
char *intersMsg = inters ? char *intersMsg = inters ?
@ -440,6 +440,24 @@ void SolveSpace::MenuAnalyze(int id) {
break; break;
} }
case GraphicsWindow::MNU_INTERFERENCE: {
SS.nakedEdges.Clear();
SMesh *m = &(SK.GetGroup(SS.GW.activeGroup)->displayMesh);
SKdNode *root = SKdNode::From(m);
bool inters, leaks;
root->MakeNakedEdgesInto(&(SS.nakedEdges), false, &inters, &leaks);
InvalidateGraphics();
if(inters) {
Error("%d edges interfere with other triangles, bad.",
SS.nakedEdges.l.n);
} else {
Message("The assembly does not interfere, good.");
}
break;
}
case GraphicsWindow::MNU_VOLUME: { case GraphicsWindow::MNU_VOLUME: {
SMesh *m = &(SK.GetGroup(SS.GW.activeGroup)->displayMesh); SMesh *m = &(SK.GetGroup(SS.GW.activeGroup)->displayMesh);
@ -501,9 +519,6 @@ void SolveSpace::MenuAnalyze(int id) {
break; break;
} }
case GraphicsWindow::MNU_INTERFERENCE:
break;
case GraphicsWindow::MNU_SHOW_DOF: case GraphicsWindow::MNU_SHOW_DOF:
// This works like a normal solve, except that it calculates // This works like a normal solve, except that it calculates
// which variables are free/bound at the same time. // which variables are free/bound at the same time.

View File

@ -227,6 +227,15 @@ void TextWindow::ScreenChangeMeshCombine(int link, DWORD v) {
SS.GenerateAll(); SS.GenerateAll();
SS.GW.ClearSuper(); SS.GW.ClearSuper();
} }
void TextWindow::ScreenChangeMeshOrExact(int link, DWORD v) {
SS.UndoRemember();
Group *g = SK.GetGroup(SS.TW.shown.group);
g->forceToMesh = !(g->forceToMesh);
SS.MarkGroupDirty(g->h);
SS.GenerateAll();
SS.GW.ClearSuper();
}
void TextWindow::ScreenChangeSuppress(int link, DWORD v) { void TextWindow::ScreenChangeSuppress(int link, DWORD v) {
SS.UndoRemember(); SS.UndoRemember();
@ -449,6 +458,25 @@ void TextWindow::ShowGroupInfo(void) {
0x80000000 | SS.modelColor[7], 7, &TextWindow::ScreenColor); 0x80000000 | SS.modelColor[7], 7, &TextWindow::ScreenColor);
} }
if(shown.group.v != Group::HGROUP_REFERENCES.v &&
(g->runningMesh.l.n > 0 ||
g->runningShell.surface.n > 0))
{
Group *pg = g->PreviousGroup();
if(pg->runningMesh.l.n == 0 && g->thisMesh.l.n == 0) {
bool fm = g->forceToMesh;
Printf(true,
"%FtSURFACES%E %Fh%f%Ll%s%E%Fs%s%E / %Fh%f%Ll%s%E%Fs%s%E",
&TextWindow::ScreenChangeMeshOrExact,
(!fm ? "" : "as NURBS"), (!fm ? "as NURBS" : ""),
&TextWindow::ScreenChangeMeshOrExact,
(fm ? "" : "as mesh"), (fm ? "as mesh" : ""));
} else {
Printf(false,
"%FtSURFACES%E %Fas mesh%FE");
}
}
// Leave more space if the group has configuration stuff above the req/ // Leave more space if the group has configuration stuff above the req/
// constraint list (as all but the drawing groups do). // constraint list (as all but the drawing groups do).
if(g->type == Group::DRAWING_3D || g->type == Group::DRAWING_WORKPLANE) { if(g->type == Group::DRAWING_3D || g->type == Group::DRAWING_WORKPLANE) {

1
ui.h
View File

@ -136,6 +136,7 @@ public:
static void ScreenChangeOneOrTwoSides(int link, DWORD v); static void ScreenChangeOneOrTwoSides(int link, DWORD v);
static void ScreenChangeSkipFirst(int link, DWORD v); static void ScreenChangeSkipFirst(int link, DWORD v);
static void ScreenChangeMeshCombine(int link, DWORD v); static void ScreenChangeMeshCombine(int link, DWORD v);
static void ScreenChangeMeshOrExact(int link, DWORD v);
static void ScreenChangeSuppress(int link, DWORD v); static void ScreenChangeSuppress(int link, DWORD v);
static void ScreenChangeRightLeftHanded(int link, DWORD v); static void ScreenChangeRightLeftHanded(int link, DWORD v);
static void ScreenChangeHelixParameter(int link, DWORD v); static void ScreenChangeHelixParameter(int link, DWORD v);