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:
parent
ddf9364257
commit
03ecbad981
@ -59,7 +59,7 @@ void Group::GenerateShellForStepAndRepeat(void) {
|
||||
ZERO(&workA);
|
||||
ZERO(&workB);
|
||||
SShell *soFar = &workA, *scratch = &workB;
|
||||
soFar->MakeFromCopyOf(src->PreviousGroupShell());
|
||||
soFar->MakeFromCopyOf(&(src->PreviousGroup()->runningShell));
|
||||
|
||||
int n = (int)valA, a0 = 0;
|
||||
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
|
||||
// but it's suppressed.
|
||||
if(suppress) {
|
||||
runningShell.MakeFromCopyOf(PreviousGroupShell());
|
||||
runningShell.MakeFromCopyOf(&(PreviousGroup()->runningShell));
|
||||
goto done;
|
||||
}
|
||||
|
||||
// So our group's shell appears in thisShell. Combine this with the
|
||||
// previous group's shell, using the requested operation.
|
||||
SShell *a = PreviousGroupShell();
|
||||
SShell *a = &(PreviousGroup()->runningShell);
|
||||
if(meshCombine == COMBINE_AS_UNION) {
|
||||
runningShell.MakeFromUnionOf(a, &thisShell);
|
||||
} else if(meshCombine == COMBINE_AS_DIFFERENCE) {
|
||||
@ -263,14 +263,14 @@ void Group::GenerateDisplayItems(void) {
|
||||
}
|
||||
}
|
||||
|
||||
SShell *Group::PreviousGroupShell(void) {
|
||||
Group *Group::PreviousGroup(void) {
|
||||
int i;
|
||||
for(i = 0; i < SK.group.n; i++) {
|
||||
Group *g = &(SK.group.elem[i]);
|
||||
if(g->h.v == h.v) break;
|
||||
}
|
||||
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) {
|
||||
|
127
mesh.cpp
127
mesh.cpp
@ -477,7 +477,7 @@ void SKdNode::MakeMeshInto(SMesh *m) {
|
||||
}
|
||||
|
||||
void SKdNode::FindEdgeOn(Vector a, Vector b, int *n, int cnt,
|
||||
bool *inter, bool *fwd)
|
||||
bool coplanarIsInter, bool *inter, bool *fwd)
|
||||
{
|
||||
if(gt && lt) {
|
||||
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 ||
|
||||
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 ||
|
||||
bc > c - KDTREE_EPS)
|
||||
{
|
||||
gt->FindEdgeOn(a, b, n, cnt, inter, fwd);
|
||||
gt->FindEdgeOn(a, b, n, cnt, coplanarIsInter, inter, fwd);
|
||||
}
|
||||
} else {
|
||||
STriangleLl *ll;
|
||||
for(ll = tris; ll; ll = ll->next) {
|
||||
STriangle *tr = ll->tri;
|
||||
return;
|
||||
}
|
||||
|
||||
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((a.Equals(tr->b) && b.Equals(tr->a)) ||
|
||||
(a.Equals(tr->c) && b.Equals(tr->b)) ||
|
||||
(a.Equals(tr->a) && b.Equals(tr->c)))
|
||||
{
|
||||
(*n)++;
|
||||
// Record whether this triangle is front- or back-facing.
|
||||
if(tr->Normal().z > LENGTH_EPS) {
|
||||
*fwd = true;
|
||||
} else {
|
||||
*fwd = false;
|
||||
}
|
||||
} 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.
|
||||
if(tr->tag == cnt) continue;
|
||||
|
||||
// Test if this triangle matches up with the given edge
|
||||
if((a.Equals(tr->b) && b.Equals(tr->a)) ||
|
||||
(a.Equals(tr->c) && b.Equals(tr->b)) ||
|
||||
(a.Equals(tr->a) && b.Equals(tr->c)))
|
||||
{
|
||||
(*n)++;
|
||||
// Record whether this triangle is front- or back-facing.
|
||||
if(tr->Normal().z > LENGTH_EPS) {
|
||||
*fwd = true;
|
||||
} else {
|
||||
// Check for self-intersection
|
||||
Vector n = (tr->Normal()).WithMagnitude(1);
|
||||
double d = (tr->a).Dot(n);
|
||||
double pa = a.Dot(n) - d, pb = b.Dot(n) - d;
|
||||
// It's an intersection if neither point lies in-plane,
|
||||
// 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)) {
|
||||
*fwd = false;
|
||||
}
|
||||
} 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 {
|
||||
// Check for self-intersection
|
||||
Vector n = (tr->Normal()).WithMagnitude(1);
|
||||
double d = (tr->a).Dot(n);
|
||||
double pa = a.Dot(n) - d, pb = b.Dot(n) - d;
|
||||
// It's an intersection if neither point lies in-plane,
|
||||
// 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;
|
||||
} 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(leaky) *leaky = false;
|
||||
|
||||
@ -699,7 +734,7 @@ void SKdNode::MakeNakedEdgesInto(SEdgeList *sel, bool *inter, bool *leaky) {
|
||||
|
||||
int n = 0, nOther = 0;
|
||||
bool thisIntersects = false, fwd;
|
||||
FindEdgeOn(a, b, &n, cnt, &thisIntersects, &fwd);
|
||||
FindEdgeOn(a, b, &n, cnt, coplanarIsInter, &thisIntersects, &fwd);
|
||||
if(n != 1) {
|
||||
sel->AddEdge(a, b);
|
||||
if(leaky) *leaky = true;
|
||||
@ -735,7 +770,7 @@ void SKdNode::MakeTurningEdgesInto(SEdgeList *sel) {
|
||||
|
||||
int n = 0;
|
||||
bool inter, fwd;
|
||||
FindEdgeOn(a, b, &n, cnt, &inter, &fwd);
|
||||
FindEdgeOn(a, b, &n, cnt, true, &inter, &fwd);
|
||||
if(n == 1) {
|
||||
// and its neighbour is front-facing, so generate the edge.
|
||||
if(fwd) sel->AddEdge(a, b);
|
||||
|
@ -224,9 +224,10 @@ public:
|
||||
void MakeMeshInto(SMesh *m);
|
||||
void ClearTags(void);
|
||||
|
||||
void FindEdgeOn(Vector a, Vector b, int *n, int cnt,
|
||||
bool *inter, bool *fwd);
|
||||
void MakeNakedEdgesInto(SEdgeList *sel, bool *inter=NULL, bool *leaky=NULL);
|
||||
void FindEdgeOn(Vector a, Vector b, int *n, int cnt, bool coplanarIsInter,
|
||||
bool *inter, bool *fwd);
|
||||
void MakeNakedEdgesInto(SEdgeList *sel, bool coplanarIsInter,
|
||||
bool *inter, bool *leaky);
|
||||
void MakeTurningEdgesInto(SEdgeList *sel);
|
||||
|
||||
void OcclusionTestLine(SEdge orig, SEdgeList *sel, int cnt);
|
||||
|
2
sketch.h
2
sketch.h
@ -207,7 +207,7 @@ public:
|
||||
bool AssembleLoops(void);
|
||||
void GenerateLoops(void);
|
||||
// And the mesh stuff
|
||||
SShell *PreviousGroupShell(void);
|
||||
Group *PreviousGroup(void);
|
||||
void GenerateShellForStepAndRepeat(void);
|
||||
void GenerateDisplayItems(void);
|
||||
void GenerateShellAndMesh(void);
|
||||
|
@ -420,7 +420,7 @@ void SolveSpace::MenuAnalyze(int id) {
|
||||
SMesh *m = &(SK.GetGroup(SS.GW.activeGroup)->displayMesh);
|
||||
SKdNode *root = SKdNode::From(m);
|
||||
bool inters, leaks;
|
||||
root->MakeNakedEdgesInto(&(SS.nakedEdges), &inters, &leaks);
|
||||
root->MakeNakedEdgesInto(&(SS.nakedEdges), true, &inters, &leaks);
|
||||
InvalidateGraphics();
|
||||
|
||||
char *intersMsg = inters ?
|
||||
@ -440,6 +440,24 @@ void SolveSpace::MenuAnalyze(int id) {
|
||||
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: {
|
||||
SMesh *m = &(SK.GetGroup(SS.GW.activeGroup)->displayMesh);
|
||||
|
||||
@ -501,9 +519,6 @@ void SolveSpace::MenuAnalyze(int id) {
|
||||
break;
|
||||
}
|
||||
|
||||
case GraphicsWindow::MNU_INTERFERENCE:
|
||||
break;
|
||||
|
||||
case GraphicsWindow::MNU_SHOW_DOF:
|
||||
// This works like a normal solve, except that it calculates
|
||||
// which variables are free/bound at the same time.
|
||||
|
@ -227,6 +227,15 @@ void TextWindow::ScreenChangeMeshCombine(int link, DWORD v) {
|
||||
SS.GenerateAll();
|
||||
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) {
|
||||
SS.UndoRemember();
|
||||
|
||||
@ -449,6 +458,25 @@ void TextWindow::ShowGroupInfo(void) {
|
||||
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/
|
||||
// constraint list (as all but the drawing groups do).
|
||||
if(g->type == Group::DRAWING_3D || g->type == Group::DRAWING_WORKPLANE) {
|
||||
|
1
ui.h
1
ui.h
@ -136,6 +136,7 @@ public:
|
||||
static void ScreenChangeOneOrTwoSides(int link, DWORD v);
|
||||
static void ScreenChangeSkipFirst(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 ScreenChangeRightLeftHanded(int link, DWORD v);
|
||||
static void ScreenChangeHelixParameter(int link, DWORD v);
|
||||
|
Loading…
Reference in New Issue
Block a user