Tear everything apart, moving away from meshes and toward shells.

Add stubs for functions to perform Booleans, and get rid of mesh
stuff, including the kd tree accelerated snap to vertex (which
should not be required if the shell triangulation performs as it
should).

Also check that a sketch is not self-intersecting before extruding
it or whatever. This is dead slow, needs n*log(n) implementation.

[git-p4: depot-paths = "//depot/solvespace/": change = 1902]
solver
Jonathan Westhues 2009-01-22 19:30:30 -08:00
parent 6d7954e167
commit bb4b767e99
13 changed files with 246 additions and 379 deletions

View File

@ -41,6 +41,7 @@ SSOBJS = $(OBJDIR)\solvespace.obj \
SRFOBJS = $(OBJDIR)\ratpoly.obj \ SRFOBJS = $(OBJDIR)\ratpoly.obj \
$(OBJDIR)\triangulate.obj \ $(OBJDIR)\triangulate.obj \
$(OBJDIR)\boolean.obj \
RES = $(OBJDIR)\resource.res RES = $(OBJDIR)\resource.res

View File

@ -86,7 +86,6 @@ void SolveSpace::ExportSectionTo(char *filename) {
// Select the naked edges in our resulting open mesh. // Select the naked edges in our resulting open mesh.
SKdNode *root = SKdNode::From(&m); SKdNode *root = SKdNode::From(&m);
root->SnapToMesh(&m);
SEdgeList el; SEdgeList el;
ZERO(&el); ZERO(&el);
root->MakeCertainEdgesInto(&el, false); root->MakeCertainEdgesInto(&el, false);
@ -294,16 +293,10 @@ void SolveSpace::ExportMeshTo(char *filename) {
Error("Active group mesh is empty; nothing to export."); Error("Active group mesh is empty; nothing to export.");
return; return;
} }
SKdNode *root = SKdNode::From(m);
root->SnapToMesh(m);
SMesh vvm;
ZERO(&vvm);
root->MakeMeshInto(&vvm);
FILE *f = fopen(filename, "wb"); FILE *f = fopen(filename, "wb");
if(!f) { if(!f) {
Error("Couldn't write to '%s'", filename); Error("Couldn't write to '%s'", filename);
vvm.Clear();
return; return;
} }
char str[80]; char str[80];
@ -311,13 +304,13 @@ void SolveSpace::ExportMeshTo(char *filename) {
strcpy(str, "STL exported mesh"); strcpy(str, "STL exported mesh");
fwrite(str, 1, 80, f); fwrite(str, 1, 80, f);
DWORD n = vvm.l.n; DWORD n = m->l.n;
fwrite(&n, 4, 1, f); fwrite(&n, 4, 1, f);
double s = SS.exportScale; double s = SS.exportScale;
int i; int i;
for(i = 0; i < vvm.l.n; i++) { for(i = 0; i < m->l.n; i++) {
STriangle *tr = &(vvm.l.elem[i]); STriangle *tr = &(m->l.elem[i]);
Vector n = tr->Normal().WithMagnitude(1); Vector n = tr->Normal().WithMagnitude(1);
float w; float w;
w = (float)n.x; fwrite(&w, 4, 1, f); w = (float)n.x; fwrite(&w, 4, 1, f);
@ -336,7 +329,6 @@ void SolveSpace::ExportMeshTo(char *filename) {
fputc(0, f); fputc(0, f);
} }
vvm.Clear();
fclose(f); fclose(f);
} }

View File

@ -208,22 +208,11 @@ void SolveSpace::GenerateAll(int first, int last, bool andFindFree) {
g->clean = true; g->clean = true;
} else { } else {
if(i >= first && i <= last) { if(i >= first && i <= last) {
// See if we have to do the vertex-to-vertex mesh, that
// we used for emphasized edges.
if(first == i &&
(g->type == Group::DRAWING_3D ||
g->type == Group::DRAWING_WORKPLANE))
{
// Special case--if the first dirty group doesn't change
// the mesh, then no need to regen edges for it.
} else {
g->vvMeshClean = false; // so we'll regen it
}
// The group falls inside the range, so really solve it, // The group falls inside the range, so really solve it,
// and then regenerate the mesh based on the solved stuff. // and then regenerate the mesh based on the solved stuff.
SolveGroup(g->h, andFindFree); SolveGroup(g->h, andFindFree);
g->GenerateLoops(); g->GenerateLoops();
g->GenerateMesh(); g->GenerateShellAndMesh();
g->clean = true; g->clean = true;
} else { } else {
// The group falls outside the range, so just assume that // The group falls outside the range, so just assume that

View File

@ -32,12 +32,17 @@ void Group::GenerateLoops(void) {
if(AssembleLoops()) { if(AssembleLoops()) {
polyError.how = POLY_GOOD; polyError.how = POLY_GOOD;
if(!poly.AllPointsInPlane(&(polyError.notCoplanarAt))) { if(!poly.AllPointsInPlane(&(polyError.errorPointAt))) {
// The edges aren't all coplanar; so not a good polygon // The edges aren't all coplanar; so not a good polygon
polyError.how = POLY_NOT_COPLANAR; polyError.how = POLY_NOT_COPLANAR;
poly.Clear(); poly.Clear();
bezierLoopSet.Clear(); bezierLoopSet.Clear();
} }
if(poly.SelfIntersecting(&(polyError.errorPointAt))) {
polyError.how = POLY_SELF_INTERSECTING;
poly.Clear();
bezierLoopSet.Clear();
}
} else { } else {
polyError.how = POLY_NOT_CLOSED; polyError.how = POLY_NOT_CLOSED;
poly.Clear(); poly.Clear();
@ -46,40 +51,9 @@ void Group::GenerateLoops(void) {
} }
} }
void Group::AddQuadWithNormal(STriMeta meta, Vector out, void Group::GenerateShellForStepAndRepeat(void) {
Vector a, Vector b, Vector c, Vector d)
{
// The quad becomes two triangles
STriangle quad1 = STriangle::From(meta, a, b, c),
quad2 = STriangle::From(meta, c, d, a);
// Could be only one of the triangles has area; be sure
// to use that one for normal checking, then.
Vector n1 = quad1.Normal(), n2 = quad2.Normal();
Vector n = (n1.Magnitude() > n2.Magnitude()) ? n1 : n2;
if(n.Dot(out) < 0) {
quad1.FlipNormal();
quad2.FlipNormal();
}
// One or both of the endpoints might lie on the axis of
// rotation, in which case its triangle is zero-area.
if(n1.Magnitude() > LENGTH_EPS) thisMesh.AddTriangle(&quad1);
if(n2.Magnitude() > LENGTH_EPS) thisMesh.AddTriangle(&quad2);
}
void Group::GenerateMeshForStepAndRepeat(void) {
Group *src = SS.GetGroup(opA); Group *src = SS.GetGroup(opA);
SMesh *srcm = &(src->thisMesh); // the mesh to step and repeat SShell *srcs = &(src->thisShell); // the shell to step and repeat
if(srcm->l.n == 0) {
runningMesh.Clear();
runningMesh.MakeFromCopy(PreviousGroupMesh());
return;
}
SMesh origm;
ZERO(&origm);
origm.MakeFromCopy(src->PreviousGroupMesh());
int n = (int)valA, a0 = 0; int n = (int)valA, a0 = 0;
if(subtype == ONE_SIDED && skipFirst) { if(subtype == ONE_SIDED && skipFirst) {
@ -90,21 +64,10 @@ void Group::GenerateMeshForStepAndRepeat(void) {
int ap = a*2 - (subtype == ONE_SIDED ? 0 : (n-1)); int ap = a*2 - (subtype == ONE_SIDED ? 0 : (n-1));
int remap = (a == (n - 1)) ? REMAP_LAST : a; int remap = (a == (n - 1)) ? REMAP_LAST : a;
thisMesh.Clear();
if(type == TRANSLATE) { if(type == TRANSLATE) {
Vector trans = Vector::From(h.param(0), h.param(1), h.param(2)); Vector trans = Vector::From(h.param(0), h.param(1), h.param(2));
trans = trans.ScaledBy(ap); trans = trans.ScaledBy(ap);
for(int i = 0; i < srcm->l.n; i++) {
STriangle tr = srcm->l.elem[i];
tr.a = (tr.a).Plus(trans);
tr.b = (tr.b).Plus(trans);
tr.c = (tr.c).Plus(trans);
if(tr.meta.face != 0) {
hEntity he = { tr.meta.face };
tr.meta.face = Remap(he, remap).v;
}
thisMesh.AddTriangle(&tr);
}
} else { } else {
Vector trans = Vector::From(h.param(0), h.param(1), h.param(2)); Vector trans = Vector::From(h.param(0), h.param(1), h.param(2));
double theta = ap * SS.GetParam(h.param(3))->val; double theta = ap * SS.GetParam(h.param(3))->val;
@ -112,46 +75,26 @@ void Group::GenerateMeshForStepAndRepeat(void) {
Vector axis = Vector::From(h.param(4), h.param(5), h.param(6)); Vector axis = Vector::From(h.param(4), h.param(5), h.param(6));
Quaternion q = Quaternion::From(c, s*axis.x, s*axis.y, s*axis.z); Quaternion q = Quaternion::From(c, s*axis.x, s*axis.y, s*axis.z);
for(int i = 0; i < srcm->l.n; i++) {
STriangle tr = srcm->l.elem[i];
tr.a = (q.Rotate((tr.a).Minus(trans))).Plus(trans);
tr.b = (q.Rotate((tr.b).Minus(trans))).Plus(trans);
tr.c = (q.Rotate((tr.c).Minus(trans))).Plus(trans);
if(tr.meta.face != 0) {
hEntity he = { tr.meta.face };
tr.meta.face = Remap(he, remap).v;
}
thisMesh.AddTriangle(&tr);
}
} }
runningMesh.Clear();
if(src->meshCombine == COMBINE_AS_DIFFERENCE) { if(src->meshCombine == COMBINE_AS_DIFFERENCE) {
runningMesh.MakeFromDifference(&origm, &thisMesh);
} else { } else {
runningMesh.MakeFromUnion(&origm, &thisMesh);
} }
origm.Clear();
origm.MakeFromCopy(&runningMesh);
} }
origm.Clear();
thisMesh.Clear();
} }
void Group::GenerateMesh(void) { void Group::GenerateShellAndMesh(void) {
thisMesh.Clear();
thisShell.Clear(); thisShell.Clear();
STriMeta meta = { 0, color }; STriMeta meta = { 0, color };
if(type == TRANSLATE || type == ROTATE) { if(type == TRANSLATE || type == ROTATE) {
GenerateMeshForStepAndRepeat(); GenerateShellForStepAndRepeat();
goto done; goto done;
} }
if(type == EXTRUDE) { if(type == EXTRUDE) {
SEdgeList edges;
ZERO(&edges);
int i;
Group *src = SS.GetGroup(opA); Group *src = SS.GetGroup(opA);
Vector translate = Vector::From(h.param(0), h.param(1), h.param(2)); Vector translate = Vector::From(h.param(0), h.param(1), h.param(2));
@ -162,112 +105,14 @@ void Group::GenerateMesh(void) {
tbot = translate.ScaledBy(-1); ttop = translate.ScaledBy(1); tbot = translate.ScaledBy(-1); ttop = translate.ScaledBy(1);
} }
thisShell = SShell::FromExtrusionOf(&(src->bezierLoopSet), tbot, ttop); thisShell.MakeFromExtrusionOf(&(src->bezierLoopSet), tbot, ttop);
thisShell.TriangulateInto(&thisMesh);
/*
bool flipBottom = translate.Dot(src->poly.normal) > 0;
// Get a triangulation of the source poly; this is not a closed mesh.
SMesh srcm; ZERO(&srcm);
(src->poly).TriangulateInto(&srcm);
// Do the bottom; that has normal pointing opposite from translate
meta.face = Remap(Entity::NO_ENTITY, REMAP_BOTTOM).v;
for(i = 0; i < srcm.l.n; i++) {
STriangle *st = &(srcm.l.elem[i]);
Vector at = (st->a).Plus(tbot),
bt = (st->b).Plus(tbot),
ct = (st->c).Plus(tbot);
if(flipBottom) {
thisMesh.AddTriangle(meta, ct, bt, at);
} else {
thisMesh.AddTriangle(meta, at, bt, ct);
}
}
// And the top; that has the normal pointing the same dir as translate
meta.face = Remap(Entity::NO_ENTITY, REMAP_TOP).v;
for(i = 0; i < srcm.l.n; i++) {
STriangle *st = &(srcm.l.elem[i]);
Vector at = (st->a).Plus(ttop),
bt = (st->b).Plus(ttop),
ct = (st->c).Plus(ttop);
if(flipBottom) {
thisMesh.AddTriangle(meta, at, bt, ct);
} else {
thisMesh.AddTriangle(meta, ct, bt, at);
}
}
srcm.Clear();
// Get the source polygon to extrude, and break it down to edges
edges.Clear();
(src->poly).MakeEdgesInto(&edges);
edges.l.ClearTags();
TagEdgesFromLineSegments(&edges);
// The sides; these are quads, represented as two triangles.
for(i = 0; i < edges.l.n; i++) {
SEdge *edge = &(edges.l.elem[i]);
Vector abot = (edge->a).Plus(tbot), bbot = (edge->b).Plus(tbot);
Vector atop = (edge->a).Plus(ttop), btop = (edge->b).Plus(ttop);
// We tagged the edges that came from line segments; their
// triangles should be associated with that plane face.
if(edge->tag) {
hEntity hl = { edge->tag };
hEntity hf = Remap(hl, REMAP_LINE_TO_FACE);
meta.face = hf.v;
} else {
meta.face = 0;
}
if(flipBottom) {
thisMesh.AddTriangle(meta, bbot, abot, atop);
thisMesh.AddTriangle(meta, bbot, atop, btop);
} else {
thisMesh.AddTriangle(meta, abot, bbot, atop);
thisMesh.AddTriangle(meta, bbot, btop, atop);
}
}
edges.Clear(); */
} else if(type == LATHE) { } else if(type == LATHE) {
SEdgeList edges;
ZERO(&edges);
int a, i;
Group *src = SS.GetGroup(opA); Group *src = SS.GetGroup(opA);
(src->poly).MakeEdgesInto(&edges);
Vector orig = SS.GetEntity(predef.origin)->PointGetNum(); Vector orig = SS.GetEntity(predef.origin)->PointGetNum();
Vector axis = SS.GetEntity(predef.entityB)->VectorGetNum(); Vector axis = SS.GetEntity(predef.entityB)->VectorGetNum();
axis = axis.WithMagnitude(1); axis = axis.WithMagnitude(1);
// Calculate the max radius, to determine fineness of mesh
double r, rmax = 0;
for(i = 0; i < edges.l.n; i++) {
SEdge *edge = &(edges.l.elem[i]);
r = (edge->a).DistanceToLine(orig, axis);
rmax = max(r, rmax);
r = (edge->b).DistanceToLine(orig, axis);
rmax = max(r, rmax);
}
int n = SS.CircleSides(rmax);
for(a = 0; a < n; a++) {
double thetai = (2*PI*WRAP(a-1, n))/n, thetaf = (2*PI*a)/n;
for(i = 0; i < edges.l.n; i++) {
SEdge *edge = &(edges.l.elem[i]);
Vector ai = (edge->a).RotatedAbout(orig, axis, thetai);
Vector bi = (edge->b).RotatedAbout(orig, axis, thetai);
Vector af = (edge->a).RotatedAbout(orig, axis, thetaf);
Vector bf = (edge->b).RotatedAbout(orig, axis, thetaf);
Vector ab = (edge->b).Minus(edge->a);
Vector out = ((src->poly).normal).Cross(ab);
// This is a vector, not a point, so no origin for rotation
out = out.RotatedAbout(axis, thetai);
AddQuadWithNormal(meta, out, ai, bi, bf, af);
}
}
} else if(type == IMPORTED) { } else if(type == IMPORTED) {
// Triangles are just copied over, with the appropriate transformation // Triangles are just copied over, with the appropriate transformation
// applied. // applied.
@ -291,7 +136,6 @@ void Group::GenerateMesh(void) {
st.a = q.Rotate(st.a).Plus(offset); st.a = q.Rotate(st.a).Plus(offset);
st.b = q.Rotate(st.b).Plus(offset); st.b = q.Rotate(st.b).Plus(offset);
st.c = q.Rotate(st.c).Plus(offset); st.c = q.Rotate(st.c).Plus(offset);
thisMesh.AddTriangle(&st);
} }
} }
@ -301,26 +145,25 @@ void Group::GenerateMesh(void) {
// If this group contributes no new mesh, then our running mesh is the // If this group contributes no new mesh, then our running mesh is the
// 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(thisMesh.l.n == 0 || suppress) { if(suppress) {
runningMesh.MakeFromCopy(PreviousGroupMesh()); runningShell.MakeFromCopyOf(PreviousGroupShell());
goto done; goto done;
} }
// So our group's mesh appears in thisMesh. Combine this with the previous // So our group's mesh appears in thisMesh. Combine this with the previous
// group's mesh, using the requested operation. // group's mesh, using the requested operation.
bool prevMeshError = meshError.yes; bool prevMeshError = meshError.yes;
meshError.yes = false; meshError.yes = false;
meshError.interferesAt.Clear(); meshError.interferesAt.Clear();
SMesh *a = PreviousGroupMesh();
SShell *a = PreviousGroupShell();
if(meshCombine == COMBINE_AS_UNION) { if(meshCombine == COMBINE_AS_UNION) {
runningMesh.MakeFromUnion(a, &thisMesh); runningShell.MakeFromUnionOf(a, &thisShell);
runningMesh = thisMesh;
ZERO(&thisMesh);
} else if(meshCombine == COMBINE_AS_DIFFERENCE) { } else if(meshCombine == COMBINE_AS_DIFFERENCE) {
runningMesh.MakeFromDifference(a, &thisMesh); runningShell.MakeFromDifferenceOf(a, &thisShell);
} else { } else {
if(!runningMesh.MakeFromInterferenceCheck(a, &thisMesh, if(0) //&(meshError.interferesAt)
&(meshError.interferesAt)))
{ {
meshError.yes = true; meshError.yes = true;
// And the list of failed triangles goes in meshError.interferesAt // And the list of failed triangles goes in meshError.interferesAt
@ -332,25 +175,21 @@ void Group::GenerateMesh(void) {
} }
done: done:
if(!vvMeshClean) { runningShell.TriangulateInto(&runningMesh);
emphEdges.Clear(); emphEdges.Clear();
if(h.v == SS.GW.activeGroup.v && SS.edgeColor != 0) { if(h.v == SS.GW.activeGroup.v && SS.edgeColor != 0) {
SKdNode *root = SKdNode::From(&runningMesh); thisShell.MakeEdgesInto(&emphEdges);
root->SnapToMesh(&runningMesh);
root->MakeCertainEdgesInto(&emphEdges, true);
}
vvMeshClean = true;
} }
} }
SMesh *Group::PreviousGroupMesh(void) { SShell *Group::PreviousGroupShell(void) {
int i; int i;
for(i = 0; i < SS.group.n; i++) { for(i = 0; i < SS.group.n; i++) {
Group *g = &(SS.group.elem[i]); Group *g = &(SS.group.elem[i]);
if(g->h.v == h.v) break; if(g->h.v == h.v) break;
} }
if(i == 0 || i >= SS.group.n) oops(); if(i == 0 || i >= SS.group.n) oops();
return &(SS.group.elem[i-1].runningMesh); return &(SS.group.elem[i-1].runningShell);
} }
void Group::Draw(void) { void Group::Draw(void) {
@ -428,15 +267,21 @@ void Group::Draw(void) {
glPopMatrix(); glPopMatrix();
glEnable(GL_DEPTH_TEST); glEnable(GL_DEPTH_TEST);
} }
} else if(polyError.how == POLY_NOT_COPLANAR) { } else if(polyError.how == POLY_NOT_COPLANAR ||
// And this one too polyError.how == POLY_SELF_INTERSECTING)
{
// These errors occur at points, not lines
if(type == DRAWING_WORKPLANE) { if(type == DRAWING_WORKPLANE) {
glDisable(GL_DEPTH_TEST); glDisable(GL_DEPTH_TEST);
glxColor3d(1, 0, 0); glxColor3d(1, 0, 0);
glPushMatrix(); glPushMatrix();
glxTranslatev(polyError.notCoplanarAt); glxTranslatev(polyError.errorPointAt);
glxOntoWorkplane(SS.GW.projRight, SS.GW.projUp); glxOntoWorkplane(SS.GW.projRight, SS.GW.projUp);
if(polyError.how == POLY_NOT_COPLANAR) {
glxWriteText("points not all coplanar!"); glxWriteText("points not all coplanar!");
} else {
glxWriteText("contour is self-intersecting!");
}
glPopMatrix(); glPopMatrix();
glEnable(GL_DEPTH_TEST); glEnable(GL_DEPTH_TEST);
} }

View File

@ -476,95 +476,6 @@ void SKdNode::MakeMeshInto(SMesh *m) {
} }
} }
void SKdNode::SnapToVertex(Vector v, SMesh *extras) {
if(gt && lt) {
double vc = v.Element(which);
if(vc < c + KDTREE_EPS) {
lt->SnapToVertex(v, extras);
}
if(vc > c - KDTREE_EPS) {
gt->SnapToVertex(v, extras);
}
// Nothing bad happens if the triangle to be split appears in both
// branches; the first call will split the triangle, so that the
// second call will do nothing, because the modified triangle will
// already contain v
} else {
STriangleLl *ll;
for(ll = tris; ll; ll = ll->next) {
STriangle *tr = ll->tri;
// Do a cheap bbox test first
int k;
bool mightHit = true;
for(k = 0; k < 3; k++) {
if((tr->a).Element(k) < v.Element(k) - KDTREE_EPS &&
(tr->b).Element(k) < v.Element(k) - KDTREE_EPS &&
(tr->c).Element(k) < v.Element(k) - KDTREE_EPS)
{
mightHit = false;
break;
}
if((tr->a).Element(k) > v.Element(k) + KDTREE_EPS &&
(tr->b).Element(k) > v.Element(k) + KDTREE_EPS &&
(tr->c).Element(k) > v.Element(k) + KDTREE_EPS)
{
mightHit = false;
break;
}
}
if(!mightHit) continue;
if(tr->a.Equals(v)) { tr->a = v; continue; }
if(tr->b.Equals(v)) { tr->b = v; continue; }
if(tr->c.Equals(v)) { tr->c = v; continue; }
if(v.OnLineSegment(tr->a, tr->b)) {
STriangle nt = STriangle::From(tr->meta, tr->a, v, tr->c);
extras->AddTriangle(&nt);
tr->a = v;
continue;
}
if(v.OnLineSegment(tr->b, tr->c)) {
STriangle nt = STriangle::From(tr->meta, tr->b, v, tr->a);
extras->AddTriangle(&nt);
tr->b = v;
continue;
}
if(v.OnLineSegment(tr->c, tr->a)) {
STriangle nt = STriangle::From(tr->meta, tr->c, v, tr->b);
extras->AddTriangle(&nt);
tr->c = v;
continue;
}
}
}
}
void SKdNode::SnapToMesh(SMesh *m) {
int i, j, k;
for(i = 0; i < m->l.n; i++) {
STriangle *tr = &(m->l.elem[i]);
for(j = 0; j < 3; j++) {
Vector v = ((j == 0) ? tr->a :
((j == 1) ? tr->b :
tr->c));
SMesh extra;
ZERO(&extra);
SnapToVertex(v, &extra);
for(k = 0; k < extra.l.n; k++) {
STriangle *tra = (STriangle *)AllocTemporary(sizeof(*tra));
*tra = extra.l.elem[k];
AddTriangle(tra);
}
extra.Clear();
}
}
}
void SKdNode::FindEdgeOn(Vector a, Vector b, int *n, int *nOther, void SKdNode::FindEdgeOn(Vector a, Vector b, int *n, int *nOther,
STriMeta m, int cnt) STriMeta m, int cnt)
{ {

View File

@ -140,21 +140,28 @@ bool SEdgeList::AssemblePolygon(SPolygon *dest, SEdge *errorAt) {
// Test if the specified edge crosses any of the edges in our list. Two edges // Test if the specified edge crosses any of the edges in our list. Two edges
// are not considered to cross if they share an endpoint (within LENGTH_EPS), // are not considered to cross if they share an endpoint (within LENGTH_EPS),
// but they are considered to cross if they are coincident and overlapping. // but they are considered to cross if they are coincident and overlapping.
// If pi is not NULL, then a crossing is returned in that.
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
bool SEdgeList::AnyEdgeCrosses(Vector a, Vector b) { int SEdgeList::AnyEdgeCrossings(Vector a, Vector b, Vector *ppi) {
Vector d = b.Minus(a); Vector d = b.Minus(a);
double t_eps = LENGTH_EPS/d.Magnitude(); double t_eps = LENGTH_EPS/d.Magnitude();
int cnt = 0;
SEdge *se; SEdge *se;
for(se = l.First(); se; se = l.NextAfter(se)) { for(se = l.First(); se; se = l.NextAfter(se)) {
double dist_a, dist_b;
double t, tse;
bool skew;
Vector pi;
bool inOrEdge0, inOrEdge1;
Vector dse = (se->b).Minus(se->a); Vector dse = (se->b).Minus(se->a);
double tse_eps = LENGTH_EPS/dse.Magnitude(); double tse_eps = LENGTH_EPS/dse.Magnitude();
if(a.Equals(se->a) && b.Equals(se->b)) return true; if(a.Equals(se->a) && b.Equals(se->b)) goto intersects;
if(b.Equals(se->a) && a.Equals(se->b)) return true; if(b.Equals(se->a) && a.Equals(se->b)) goto intersects;
double dist_a = (se->a).DistanceToLine(a, d), dist_a = (se->a).DistanceToLine(a, d),
dist_b = (se->b).DistanceToLine(a, d); dist_b = (se->b).DistanceToLine(a, d);
if(fabs(dist_a - dist_b) < LENGTH_EPS) { if(fabs(dist_a - dist_b) < LENGTH_EPS) {
@ -167,28 +174,25 @@ bool SEdgeList::AnyEdgeCrosses(Vector a, Vector b) {
// on the other // on the other
double t; double t;
t = ((se->a).Minus(a)).DivPivoting(d); t = ((se->a).Minus(a)).DivPivoting(d);
if(t > t_eps && t < (1 - t_eps)) return true; if(t > t_eps && t < (1 - t_eps)) goto intersects;
t = ((se->b).Minus(a)).DivPivoting(d); t = ((se->b).Minus(a)).DivPivoting(d);
if(t > t_eps && t < (1 - t_eps)) return true; if(t > t_eps && t < (1 - t_eps)) goto intersects;
t = a.Minus(se->a).DivPivoting(dse); t = a.Minus(se->a).DivPivoting(dse);
if(t > tse_eps && t < (1 - tse_eps)) return true; if(t > tse_eps && t < (1 - tse_eps)) goto intersects;
t = b.Minus(se->a).DivPivoting(dse); t = b.Minus(se->a).DivPivoting(dse);
if(t > tse_eps && t < (1 - tse_eps)) return true; if(t > tse_eps && t < (1 - tse_eps)) goto intersects;
// So coincident but disjoint, okay. // So coincident but disjoint, okay.
continue; continue;
} }
// Lines are not parallel, so look for an intersection. // Lines are not parallel, so look for an intersection.
double t, tse; pi = Vector::AtIntersectionOfLines(a, b, se->a, se->b,
bool skew;
Vector pi = Vector::AtIntersectionOfLines(a, b,
se->a, se->b,
&skew, &skew,
&t, &tse); &t, &tse);
if(skew) continue; if(skew) continue;
bool inOrEdge0 = (t > -t_eps) && (t < (1 + t_eps)); inOrEdge0 = (t > -t_eps) && (t < (1 + t_eps));
bool inOrEdge1 = (tse > -tse_eps) && (tse < (1 + tse_eps)); inOrEdge1 = (tse > -tse_eps) && (tse < (1 + tse_eps));
if(inOrEdge0 && inOrEdge1) { if(inOrEdge0 && inOrEdge1) {
if((se->a).Equals(a) || (se->b).Equals(a) || if((se->a).Equals(a) || (se->b).Equals(a) ||
@ -200,10 +204,16 @@ bool SEdgeList::AnyEdgeCrosses(Vector a, Vector b) {
// But it's an intersection if a vertex of one edge lies on the // But it's an intersection if a vertex of one edge lies on the
// inside of the other (or if they cross away from either's // inside of the other (or if they cross away from either's
// vertex). // vertex).
return true; if(ppi) *ppi = pi;
goto intersects;
} }
continue;
intersects:
cnt++;
// and continue with the loop
} }
return false; return cnt;
} }
void SContour::AddPoint(Vector p) { void SContour::AddPoint(Vector p) {
@ -416,6 +426,19 @@ bool SPolygon::AllPointsInPlane(Vector *notCoplanarAt) {
return true; return true;
} }
bool SPolygon::SelfIntersecting(Vector *intersectsAt) {
SEdgeList el;
ZERO(&el);
MakeEdgesInto(&el);
SEdge *se;
for(se = el.l.First(); se; se = el.l.NextAfter(se)) {
int inters = el.AnyEdgeCrossings(se->a, se->b, intersectsAt);
if(inters != 1) return true;
}
return false;
}
static int TriMode, TriVertexCount; static int TriMode, TriVertexCount;
static Vector Tri1, TriNMinus1, TriNMinus2; static Vector Tri1, TriNMinus1, TriNMinus2;
static Vector TriNormal; static Vector TriNormal;

View File

@ -24,7 +24,7 @@ public:
bool AssemblePolygon(SPolygon *dest, SEdge *errorAt); bool AssemblePolygon(SPolygon *dest, SEdge *errorAt);
bool AssembleContour(Vector first, Vector last, SContour *dest, bool AssembleContour(Vector first, Vector last, SContour *dest,
SEdge *errorAt); SEdge *errorAt);
bool AnyEdgeCrosses(Vector a, Vector b); int AnyEdgeCrossings(Vector a, Vector b, Vector *pi=NULL);
}; };
class SPoint { class SPoint {
@ -83,6 +83,7 @@ public:
void TriangulateInto(SMesh *m, STriMeta meta); void TriangulateInto(SMesh *m, STriMeta meta);
void Clear(void); void Clear(void);
bool AllPointsInPlane(Vector *notCoplanarAt); bool AllPointsInPlane(Vector *notCoplanarAt);
bool SelfIntersecting(Vector *intersectsAt);
bool IsEmpty(void); bool IsEmpty(void);
Vector AnyPoint(void); Vector AnyPoint(void);
void OffsetInto(SPolygon *dest, double r); void OffsetInto(SPolygon *dest, double r);
@ -220,9 +221,6 @@ public:
void FindEdgeOn(Vector a, Vector b, int *n, int *nOther, void FindEdgeOn(Vector a, Vector b, int *n, int *nOther,
STriMeta m, int cnt); STriMeta m, int cnt);
void MakeCertainEdgesInto(SEdgeList *sel, bool emphasized); void MakeCertainEdgesInto(SEdgeList *sel, bool emphasized);
void SnapToMesh(SMesh *m);
void SnapToVertex(Vector v, SMesh *extras);
}; };
#endif #endif

View File

@ -141,21 +141,21 @@ public:
static const int POLY_GOOD = 0; static const int POLY_GOOD = 0;
static const int POLY_NOT_CLOSED = 1; static const int POLY_NOT_CLOSED = 1;
static const int POLY_NOT_COPLANAR = 2; static const int POLY_NOT_COPLANAR = 2;
static const int POLY_SELF_INTERSECTING = 3;
struct { struct {
int how; int how;
SEdge notClosedAt; SEdge notClosedAt;
Vector notCoplanarAt; Vector errorPointAt;
} polyError; } polyError;
SMesh thisMesh; SShell thisShell;
SShell runningShell;
SMesh runningMesh; SMesh runningMesh;
struct { struct {
SMesh interferesAt; SMesh interferesAt;
bool yes; bool yes;
} meshError; } meshError;
SEdgeList emphEdges; SEdgeList emphEdges;
SShell thisShell;
SShell runningShell;
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;
@ -206,11 +206,9 @@ public:
bool AssembleLoops(void); bool AssembleLoops(void);
void GenerateLoops(void); void GenerateLoops(void);
// And the mesh stuff // And the mesh stuff
SMesh *PreviousGroupMesh(void); SShell *PreviousGroupShell(void);
void AddQuadWithNormal(STriMeta meta, Vector out, void GenerateShellForStepAndRepeat(void);
Vector a, Vector b, Vector c, Vector d); void GenerateShellAndMesh(void);
void GenerateMeshForStepAndRepeat(void);
void GenerateMesh(void);
void Draw(void); void Draw(void);
SPolygon GetPolygon(void); SPolygon GetPolygon(void);

10
srf/boolean.cpp Normal file
View File

@ -0,0 +1,10 @@
#include "solvespace.h"
void SShell::MakeFromUnionOf(SShell *a, SShell *b) {
MakeFromCopyOf(b);
}
void SShell::MakeFromDifferenceOf(SShell *a, SShell *b) {
MakeFromCopyOf(b);
}

View File

@ -147,6 +147,14 @@ void SBezier::Reverse(void) {
} }
} }
SBezier SBezier::TransformedBy(Vector t, Quaternion q) {
SBezier ret = *this;
int i;
for(i = 0; i <= deg; i++) {
ret.ctrl[i] = (q.Rotate(ret.ctrl[i])).Plus(t);
}
return ret;
}
void SBezierList::Clear(void) { void SBezierList::Clear(void) {
l.Clear(); l.Clear();
@ -292,6 +300,22 @@ void SBezierLoopSet::Clear(void) {
l.Clear(); l.Clear();
} }
SCurve SCurve::FromTransformationOf(SCurve *a, Vector t, Quaternion q) {
SCurve ret;
ZERO(&ret);
ret.h = a->h;
ret.isExact = a->isExact;
ret.exact = (a->exact).TransformedBy(t, q);
Vector *p;
for(p = a->pts.First(); p; p = a->pts.NextAfter(p)) {
Vector pp = (q.Rotate(*p)).Plus(t);
ret.pts.Add(&pp);
}
return ret;
}
void SCurve::Clear(void) { void SCurve::Clear(void) {
pts.Clear(); pts.Clear();
} }
@ -346,6 +370,37 @@ SSurface SSurface::FromPlane(Vector pt, Vector n) {
return ret; return ret;
} }
SSurface SSurface::FromTransformationOf(SSurface *a, Vector t, Quaternion q,
bool includingTrims)
{
SSurface ret;
ZERO(&ret);
ret.h = a->h;
ret.degm = a->degm;
ret.degn = a->degn;
int i, j;
for(i = 0; i <= 3; i++) {
for(j = 0; j <= 3; j++) {
ret.ctrl[i][j] = (q.Rotate(a->ctrl[i][j])).Plus(t);
ret.weight[i][j] = a->weight[i][j];
}
}
if(includingTrims) {
STrimBy *stb;
for(stb = a->trim.First(); stb; stb = a->trim.NextAfter(stb)) {
STrimBy n = *stb;
n.start = (q.Rotate(n.start)) .Plus(t);
n.finish = (q.Rotate(n.finish)).Plus(t);
ret.trim.Add(&n);
}
}
return ret;
}
Vector SSurface::PointAt(double u, double v) { Vector SSurface::PointAt(double u, double v) {
Vector num = Vector::From(0, 0, 0); Vector num = Vector::From(0, 0, 0);
double den = 0; double den = 0;
@ -449,31 +504,41 @@ void SSurface::ClosestPointTo(Vector p, double *u, double *v) {
} }
} }
void SSurface::TriangulateInto(SShell *shell, SMesh *sm) { void SSurface::MakeEdgesInto(SShell *shell, SEdgeList *sel, bool asUv) {
SEdgeList el;
ZERO(&el);
STrimBy *stb; STrimBy *stb;
for(stb = trim.First(); stb; stb = trim.NextAfter(stb)) { for(stb = trim.First(); stb; stb = trim.NextAfter(stb)) {
SCurve *sc = shell->curve.FindById(stb->curve); SCurve *sc = shell->curve.FindById(stb->curve);
Vector prevuv, ptuv; Vector prev, prevuv, ptuv;
bool inCurve = false; bool inCurve = false;
Vector *pt; Vector *pt;
double u = 0, v = 0; double u = 0, v = 0;
for(pt = sc->pts.First(); pt; pt = sc->pts.NextAfter(pt)) { for(pt = sc->pts.First(); pt; pt = sc->pts.NextAfter(pt)) {
if(asUv) {
ClosestPointTo(*pt, &u, &v); ClosestPointTo(*pt, &u, &v);
ptuv = Vector::From(u, v, 0); ptuv = Vector::From(u, v, 0);
if(inCurve) { if(inCurve) {
el.AddEdge(prevuv, ptuv); sel->AddEdge(prevuv, ptuv);
} }
prevuv = ptuv; prevuv = ptuv;
} else {
if(inCurve) {
sel->AddEdge(prev, *pt);
}
prev = *pt;
}
if(pt->EqualsExactly(stb->start)) inCurve = true; if(pt->EqualsExactly(stb->start)) inCurve = true;
if(pt->EqualsExactly(stb->finish)) inCurve = false; if(pt->EqualsExactly(stb->finish)) inCurve = false;
} }
} }
}
void SSurface::TriangulateInto(SShell *shell, SMesh *sm) {
SEdgeList el;
ZERO(&el);
MakeEdgesInto(shell, &el, true);
SPolygon poly; SPolygon poly;
ZERO(&poly); ZERO(&poly);
@ -508,9 +573,8 @@ void SSurface::Clear(void) {
trim.Clear(); trim.Clear();
} }
SShell SShell::FromExtrusionOf(SBezierLoopSet *sbls, Vector t0, Vector t1) { void SShell::MakeFromExtrusionOf(SBezierLoopSet *sbls, Vector t0, Vector t1) {
SShell ret; ZERO(this);
ZERO(&ret);
// Make the extrusion direction consistent with respect to the normal // Make the extrusion direction consistent with respect to the normal
// of the sketch we're extruding. // of the sketch we're extruding.
@ -523,8 +587,8 @@ SShell SShell::FromExtrusionOf(SBezierLoopSet *sbls, Vector t0, Vector t1) {
SSurface s0, s1; SSurface s0, s1;
s0 = SSurface::FromPlane(sbls->point.Plus(t0), sbls->normal.ScaledBy(-1)); s0 = SSurface::FromPlane(sbls->point.Plus(t0), sbls->normal.ScaledBy(-1));
s1 = SSurface::FromPlane(sbls->point.Plus(t1), sbls->normal.ScaledBy( 1)); s1 = SSurface::FromPlane(sbls->point.Plus(t1), sbls->normal.ScaledBy( 1));
hSSurface hs0 = ret.surface.AddAndAssignId(&s0), hSSurface hs0 = surface.AddAndAssignId(&s0),
hs1 = ret.surface.AddAndAssignId(&s1); hs1 = surface.AddAndAssignId(&s1);
// Now go through the input curves. For each one, generate its surface // Now go through the input curves. For each one, generate its surface
// of extrusion, its two translated trim curves, and one trim line. We // of extrusion, its two translated trim curves, and one trim line. We
@ -544,27 +608,27 @@ SShell SShell::FromExtrusionOf(SBezierLoopSet *sbls, Vector t0, Vector t1) {
// Generate the surface of extrusion of this curve, and add // Generate the surface of extrusion of this curve, and add
// it to the list // it to the list
SSurface ss = SSurface::FromExtrusionOf(sb, t0, t1); SSurface ss = SSurface::FromExtrusionOf(sb, t0, t1);
hSSurface hsext = ret.surface.AddAndAssignId(&ss); hSSurface hsext = surface.AddAndAssignId(&ss);
// Translate the curve by t0 and t1 to produce two trim curves // Translate the curve by t0 and t1 to produce two trim curves
SCurve sc; SCurve sc;
ZERO(&sc); ZERO(&sc);
sb->MakePwlInto(&(sc.pts), t0); sb->MakePwlInto(&(sc.pts), t0);
hSCurve hc0 = ret.curve.AddAndAssignId(&sc); hSCurve hc0 = curve.AddAndAssignId(&sc);
STrimBy stb0 = STrimBy::EntireCurve(&ret, hc0); STrimBy stb0 = STrimBy::EntireCurve(this, hc0);
ZERO(&sc); ZERO(&sc);
sb->MakePwlInto(&(sc.pts), t1); sb->MakePwlInto(&(sc.pts), t1);
hSCurve hc1 = ret.curve.AddAndAssignId(&sc); hSCurve hc1 = curve.AddAndAssignId(&sc);
STrimBy stb1 = STrimBy::EntireCurve(&ret, hc1); STrimBy stb1 = STrimBy::EntireCurve(this, hc1);
// The translated curves trim the flat top and bottom surfaces. // The translated curves trim the flat top and bottom surfaces.
// (ret.surface.FindById(hs0))->trim.Add(&stb0); (surface.FindById(hs0))->trim.Add(&stb0);
(ret.surface.FindById(hs1))->trim.Add(&stb1); (surface.FindById(hs1))->trim.Add(&stb1);
// The translated curves also trim the surface of extrusion. // The translated curves also trim the surface of extrusion.
// (ret.surface.FindById(hsext))->trim.Add(&stb0); (surface.FindById(hsext))->trim.Add(&stb0);
// (ret.surface.FindById(hsext))->trim.Add(&stb1); (surface.FindById(hsext))->trim.Add(&stb1);
// And form the trim line // And form the trim line
Vector pt = sb->Finish(); Vector pt = sb->Finish();
@ -572,10 +636,10 @@ SShell SShell::FromExtrusionOf(SBezierLoopSet *sbls, Vector t0, Vector t1) {
ZERO(&sc); ZERO(&sc);
sc.pts.Add(&p0); sc.pts.Add(&p0);
sc.pts.Add(&p1); sc.pts.Add(&p1);
hSCurve hl = ret.curve.AddAndAssignId(&sc); hSCurve hl = curve.AddAndAssignId(&sc);
// save this for later // save this for later
TrimLine tl; TrimLine tl;
tl.trim = STrimBy::EntireCurve(&ret, hl); tl.trim = STrimBy::EntireCurve(this, hl);
tl.hs = hsext; tl.hs = hsext;
trimLines.Add(&tl); trimLines.Add(&tl);
} }
@ -583,17 +647,45 @@ SShell SShell::FromExtrusionOf(SBezierLoopSet *sbls, Vector t0, Vector t1) {
int i; int i;
for(i = 0; i < trimLines.n; i++) { for(i = 0; i < trimLines.n; i++) {
TrimLine *tl = &(trimLines.elem[i]); TrimLine *tl = &(trimLines.elem[i]);
SSurface *ss = ret.surface.FindById(tl->hs); SSurface *ss = surface.FindById(tl->hs);
TrimLine *tlp = &(trimLines.elem[WRAP(i-1, trimLines.n)]); TrimLine *tlp = &(trimLines.elem[WRAP(i-1, trimLines.n)]);
// ss->trim.Add(&(tl->trim)); ss->trim.Add(&(tl->trim));
// ss->trim.Add(&(tlp->trim)); ss->trim.Add(&(tlp->trim));
} }
trimLines.Clear(); trimLines.Clear();
} }
}
return ret; void SShell::MakeFromCopyOf(SShell *a) {
Vector t = Vector::From(0, 0, 0);
Quaternion q = Quaternion::From(1, 0, 0, 0);
MakeFromTransformationOf(a, t, q);
}
void SShell::MakeFromTransformationOf(SShell *a, Vector t, Quaternion q) {
SSurface *s;
for(s = a->surface.First(); s; s = a->surface.NextAfter(s)) {
SSurface n;
n = SSurface::FromTransformationOf(s, t, q, true);
surface.Add(&n); // keeping the old ID
}
SCurve *c;
for(c = a->curve.First(); c; c = a->curve.NextAfter(c)) {
SCurve n;
n = SCurve::FromTransformationOf(c, t, q);
curve.Add(&n); // keeping the old ID
}
}
void SShell::MakeEdgesInto(SEdgeList *sel) {
SSurface *s;
for(s = surface.First(); s; s = surface.NextAfter(s)) {
s->MakeEdgesInto(this, sel, false);
}
} }
void SShell::TriangulateInto(SMesh *sm) { void SShell::TriangulateInto(SMesh *sm) {

View File

@ -36,6 +36,8 @@ public:
void Reverse(void); void Reverse(void);
SBezier TransformedBy(Vector t, Quaternion q);
static SBezier From(Vector p0, Vector p1, Vector p2, Vector p3); static SBezier From(Vector p0, Vector p1, Vector p2, Vector p3);
static SBezier From(Vector p0, Vector p1, Vector p2); static SBezier From(Vector p0, Vector p1, Vector p2);
static SBezier From(Vector p0, Vector p1); static SBezier From(Vector p0, Vector p1);
@ -82,6 +84,9 @@ public:
List<Vector> pts; List<Vector> pts;
static SCurve SCurve::FromTransformationOf(SCurve *a,
Vector t, Quaternion q);
void Clear(void); void Clear(void);
}; };
@ -112,6 +117,8 @@ public:
static SSurface FromExtrusionOf(SBezier *spc, Vector t0, Vector t1); static SSurface FromExtrusionOf(SBezier *spc, Vector t0, Vector t1);
static SSurface FromPlane(Vector pt, Vector n); static SSurface FromPlane(Vector pt, Vector n);
static SSurface FromTransformationOf(SSurface *a, Vector t, Quaternion q,
bool includingTrims);
void ClosestPointTo(Vector p, double *u, double *v); void ClosestPointTo(Vector p, double *u, double *v);
Vector PointAt(double u, double v); Vector PointAt(double u, double v);
@ -119,6 +126,7 @@ public:
Vector NormalAt(double u, double v); Vector NormalAt(double u, double v);
void TriangulateInto(SShell *shell, SMesh *sm); void TriangulateInto(SShell *shell, SMesh *sm);
void MakeEdgesInto(SShell *shell, SEdgeList *sel, bool asUv);
void Clear(void); void Clear(void);
}; };
@ -128,11 +136,14 @@ public:
IdList<SCurve,hSCurve> curve; IdList<SCurve,hSCurve> curve;
IdList<SSurface,hSSurface> surface; IdList<SSurface,hSSurface> surface;
static SShell FromExtrusionOf(SBezierLoopSet *sbls, Vector t0, Vector t1); void MakeFromExtrusionOf(SBezierLoopSet *sbls, Vector t0, Vector t1);
void MakeFromUnionOf(SShell *a, SShell *b);
static SShell FromUnionOf(SShell *a, SShell *b); void MakeFromDifferenceOf(SShell *a, SShell *b);
void MakeFromCopyOf(SShell *a);
void MakeFromTransformationOf(SShell *a, Vector trans, Quaternion q);
void TriangulateInto(SMesh *sm); void TriangulateInto(SMesh *sm);
void MakeEdgesInto(SEdgeList *sel);
void Clear(void); void Clear(void);
}; };

View File

@ -133,7 +133,7 @@ bool SContour::BridgeToContour(SContour *sc,
} }
if(f) continue; if(f) continue;
if(avoidEdges->AnyEdgeCrosses(a, b)) { if(avoidEdges->AnyEdgeCrossings(a, b) > 0) {
// doesn't work, bridge crosses an existing edge // doesn't work, bridge crosses an existing edge
} else { } else {
goto haveEdge; goto haveEdge;

View File

@ -46,12 +46,10 @@ void SolveSpace::PushFromCurrentOnto(UndoStack *uk) {
// And then clean up all the stuff that needs to be a deep copy, // 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. // and zero out all the dynamic stuff that will get regenerated.
dest.clean = false; dest.clean = false;
dest.vvMeshClean = false;
ZERO(&(dest.solved)); ZERO(&(dest.solved));
ZERO(&(dest.poly)); ZERO(&(dest.poly));
ZERO(&(dest.bezierLoopSet)); ZERO(&(dest.bezierLoopSet));
ZERO(&(dest.polyError)); ZERO(&(dest.polyError));
ZERO(&(dest.thisMesh));
ZERO(&(dest.runningMesh)); ZERO(&(dest.runningMesh));
ZERO(&(dest.thisShell)); ZERO(&(dest.thisShell));
ZERO(&(dest.runningShell)); ZERO(&(dest.runningShell));
@ -95,7 +93,6 @@ void SolveSpace::PopOntoCurrentFrom(UndoStack *uk) {
Group *g = &(group.elem[i]); Group *g = &(group.elem[i]);
g->poly.Clear(); g->poly.Clear();
g->bezierLoopSet.Clear(); g->bezierLoopSet.Clear();
g->thisMesh.Clear();
g->runningMesh.Clear(); g->runningMesh.Clear();
g->thisShell.Clear(); g->thisShell.Clear();
g->runningShell.Clear(); g->runningShell.Clear();