Add an interference check for assembled parts. That's easy once the

BSP stuff works. The failures are reported with red stripes and no
depth buffering, and in a message in the text window.

Also improve convergence of point-on-line constraints, and don't
write triangles to export files with limited precision, because
that was making the coplanar tests fail.

[git-p4: depot-paths = "//depot/solvespace/": change = 1774]
solver
Jonathan Westhues 2008-06-03 22:39:32 -08:00
parent 64c7a4e61b
commit 71391e6a55
7 changed files with 76 additions and 8 deletions

View File

@ -567,14 +567,21 @@ void Constraint::Generate(IdList<Equation,hEquation> *l) {
ExprVector ea = a->PointGetExprs(); ExprVector ea = a->PointGetExprs();
ExprVector eb = b->PointGetExprs(); ExprVector eb = b->PointGetExprs();
ExprVector eab = ea.Minus(eb); ExprVector eab = ea.Minus(eb);
// Construct a vector from the point to either endpoint of
// the line segment, and choose the longer of these.
ExprVector eap = ea.Minus(ep); ExprVector eap = ea.Minus(ep);
ExprVector ebp = eb.Minus(ep);
ExprVector elp =
(ebp.Magnitude()->Eval() > eap.Magnitude()->Eval()) ?
ebp : eap;
if(p->group.v == group.v) { if(p->group.v == group.v) {
AddEq(l, VectorsParallel(0, eab, eap), 0); AddEq(l, VectorsParallel(0, eab, elp), 0);
AddEq(l, VectorsParallel(1, eab, eap), 1); AddEq(l, VectorsParallel(1, eab, elp), 1);
} else { } else {
AddEq(l, VectorsParallel(0, eap, eab), 0); AddEq(l, VectorsParallel(0, elp, eab), 0);
AddEq(l, VectorsParallel(1, eap, eab), 1); AddEq(l, VectorsParallel(1, elp, eab), 1);
} }
} else { } else {
AddEq(l, PointLineDistance(workplane, ptA, entityA), 0); AddEq(l, PointLineDistance(workplane, ptA, entityA), 0);

View File

@ -234,7 +234,7 @@ bool SolveSpace::SaveToFile(char *filename) {
double mag = tr->Normal().Magnitude(); double mag = tr->Normal().Magnitude();
dbp("triangle: mag=%.5f", mag); */ dbp("triangle: mag=%.5f", mag); */
fprintf(fh, "Triangle %08x %08x " fprintf(fh, "Triangle %08x %08x "
"%.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f\n", "%.20f %.20f %.20f %.20f %.20f %.20f %.20f %.20f %.20f\n",
tr->meta.face, tr->meta.color, tr->meta.face, tr->meta.color,
CO(tr->a), CO(tr->b), CO(tr->c)); CO(tr->a), CO(tr->b), CO(tr->c));
} }

View File

@ -225,7 +225,7 @@ void SMesh::MakeFromDifference(SMesh *a, SMesh *b) {
SBsp3 *bspb = SBsp3::FromMesh(b); SBsp3 *bspb = SBsp3::FromMesh(b);
flipNormal = true; flipNormal = true;
keepCoplanar = false; keepCoplanar = true;
AddAgainstBsp(b, bspa); AddAgainstBsp(b, bspa);
flipNormal = false; flipNormal = false;
@ -235,6 +235,31 @@ void SMesh::MakeFromDifference(SMesh *a, SMesh *b) {
dbp("tris = %d", l.n); dbp("tris = %d", l.n);
} }
bool SMesh::MakeFromInterferenceCheck(SMesh *srca, SMesh *srcb, SMesh *error) {
SBsp3 *bspa = SBsp3::FromMesh(srca);
SBsp3 *bspb = SBsp3::FromMesh(srcb);
error->Clear();
error->flipNormal = true;
error->keepCoplanar = false;
error->AddAgainstBsp(srcb, bspa);
error->AddAgainstBsp(srca, bspb);
// Now we have a list of all the triangles (or fragments thereof) from
// A that lie inside B, or vice versa. That's the interference, and
// we report it so that it can be flagged.
// But as far as the actual model, we just copy everything over.
int i;
for(i = 0; i < srca->l.n; i++) {
AddTriangle(&(srca->l.elem[i]));
}
for(i = 0; i < srcb->l.n; i++) {
AddTriangle(&(srcb->l.elem[i]));
}
return (error->l.n == 0);
}
DWORD SMesh::FirstIntersectionWith(Point2d mp) { DWORD SMesh::FirstIntersectionWith(Point2d mp) {
Vector gu = SS.GW.projRight, gv = SS.GW.projUp; Vector gu = SS.GW.projRight, gv = SS.GW.projUp;
Vector gn = (gu.Cross(gv)).WithMagnitude(1); Vector gn = (gu.Cross(gv)).WithMagnitude(1);
@ -325,7 +350,9 @@ void SBsp3::InsertInPlane(bool pos2, STriangle *tr, SMesh *m) {
ll = ll->more; ll = ll->more;
} }
if(m->flipNormal && ((!pos2 && !onFace) || (onFace && !sameNormal))) { if(m->flipNormal && ((!pos2 && !onFace) ||
(onFace && !sameNormal && m->keepCoplanar)))
{
m->AddTriangle(tr->meta, tr->c, tr->b, tr->a); m->AddTriangle(tr->meta, tr->c, tr->b, tr->a);
} else if(!(m->flipNormal) && ((pos2 && !onFace) || } else if(!(m->flipNormal) && ((pos2 && !onFace) ||
(onFace && sameNormal && m->keepCoplanar))) (onFace && sameNormal && m->keepCoplanar)))

View File

@ -191,6 +191,7 @@ public:
void AddAgainstBsp(SMesh *srcm, SBsp3 *bsp3); void AddAgainstBsp(SMesh *srcm, SBsp3 *bsp3);
void MakeFromUnion(SMesh *a, SMesh *b); void MakeFromUnion(SMesh *a, SMesh *b);
void MakeFromDifference(SMesh *a, SMesh *b); void MakeFromDifference(SMesh *a, SMesh *b);
bool MakeFromInterferenceCheck(SMesh *srca, SMesh *srcb, SMesh *errorAt);
DWORD FirstIntersectionWith(Point2d mp); DWORD FirstIntersectionWith(Point2d mp);
}; };

View File

@ -737,13 +737,22 @@ void Group::GenerateMesh(void) {
// So our group's mesh appears in outm. Combine this with the previous // So our group's mesh appears in outm. Combine this with the previous
// group's mesh, using the requested operation. // group's mesh, using the requested operation.
mesh.Clear(); mesh.Clear();
bool prevMeshError = meshError.yes;
meshError.yes = false;
meshError.interferesAt.Clear();
SMesh *a = PreviousGroupMesh(); SMesh *a = PreviousGroupMesh();
if(meshCombine == COMBINE_AS_UNION) { if(meshCombine == COMBINE_AS_UNION) {
mesh.MakeFromUnion(a, &outm); mesh.MakeFromUnion(a, &outm);
} else if(meshCombine == COMBINE_AS_DIFFERENCE) { } else if(meshCombine == COMBINE_AS_DIFFERENCE) {
mesh.MakeFromDifference(a, &outm); mesh.MakeFromDifference(a, &outm);
} else { } else {
if(!mesh.MakeFromInterferenceCheck(a, &outm, &(meshError.interferesAt)))
meshError.yes = true;
// And the list of failed triangles appears in meshError.interferesAt
}
if(prevMeshError != meshError.yes) {
// The error is reported in the text window for the group.
SS.later.showTW = true;
} }
outm.Clear(); outm.Clear();
} }
@ -789,6 +798,23 @@ void Group::Draw(void) {
if(SS.GW.showShaded) glxFillMesh(specColor, &mesh, mh, ms1, ms2); if(SS.GW.showShaded) glxFillMesh(specColor, &mesh, mh, ms1, ms2);
glDisable(GL_LIGHTING); glDisable(GL_LIGHTING);
if(meshError.yes) {
// Draw the error triangles in bright red stripes, with no Z buffering
GLubyte mask[32*32/8];
memset(mask, 0xf0, sizeof(mask));
glPolygonStipple(mask);
int specColor = 0;
glDisable(GL_DEPTH_TEST);
glColor3d(0, 0, 0);
glxFillMesh(0, &meshError.interferesAt, 0, 0, 0);
glEnable(GL_POLYGON_STIPPLE);
glColor3d(1, 0, 0);
glxFillMesh(0, &meshError.interferesAt, 0, 0, 0);
glEnable(GL_DEPTH_TEST);
glDisable(GL_POLYGON_STIPPLE);
}
if(SS.GW.showMesh) glxDebugMesh(&mesh); if(SS.GW.showMesh) glxDebugMesh(&mesh);
if(!SS.GW.showShaded) return; if(!SS.GW.showShaded) return;

View File

@ -124,6 +124,10 @@ public:
bool yes; bool yes;
} polyError; } polyError;
SMesh mesh; SMesh mesh;
struct {
SMesh interferesAt;
bool yes;
} meshError;
static const int COMBINE_AS_UNION = 0; static const int COMBINE_AS_UNION = 0;
static const int COMBINE_AS_DIFFERENCE = 1; static const int COMBINE_AS_DIFFERENCE = 1;

View File

@ -656,6 +656,9 @@ void TextWindow::ShowGroupInfo(void) {
Group::COMBINE_AS_ASSEMBLE, Group::COMBINE_AS_ASSEMBLE,
(asy || !asa ? "" : "assemble"), (asy && asa ? "assemble" : "")); (asy || !asa ? "" : "assemble"), (asy && asa ? "assemble" : ""));
} }
if(g->type == Group::IMPORTED && g->meshError.yes) {
Printf(false, "%Fx the parts interfere!");
}
if(g->type == Group::EXTRUDE) { if(g->type == Group::EXTRUDE) {
#define TWOX(v) v v #define TWOX(v) v v