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 \
$(OBJDIR)\triangulate.obj \
$(OBJDIR)\boolean.obj \
RES = $(OBJDIR)\resource.res

View File

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

View File

@ -208,22 +208,11 @@ void SolveSpace::GenerateAll(int first, int last, bool andFindFree) {
g->clean = true;
} else {
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,
// and then regenerate the mesh based on the solved stuff.
SolveGroup(g->h, andFindFree);
g->GenerateLoops();
g->GenerateMesh();
g->GenerateShellAndMesh();
g->clean = true;
} else {
// The group falls outside the range, so just assume that

View File

@ -32,12 +32,17 @@ void Group::GenerateLoops(void) {
if(AssembleLoops()) {
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
polyError.how = POLY_NOT_COPLANAR;
poly.Clear();
bezierLoopSet.Clear();
}
if(poly.SelfIntersecting(&(polyError.errorPointAt))) {
polyError.how = POLY_SELF_INTERSECTING;
poly.Clear();
bezierLoopSet.Clear();
}
} else {
polyError.how = POLY_NOT_CLOSED;
poly.Clear();
@ -46,40 +51,9 @@ void Group::GenerateLoops(void) {
}
}
void Group::AddQuadWithNormal(STriMeta meta, Vector out,
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) {
void Group::GenerateShellForStepAndRepeat(void) {
Group *src = SS.GetGroup(opA);
SMesh *srcm = &(src->thisMesh); // the mesh to step and repeat
if(srcm->l.n == 0) {
runningMesh.Clear();
runningMesh.MakeFromCopy(PreviousGroupMesh());
return;
}
SMesh origm;
ZERO(&origm);
origm.MakeFromCopy(src->PreviousGroupMesh());
SShell *srcs = &(src->thisShell); // the shell to step and repeat
int n = (int)valA, a0 = 0;
if(subtype == ONE_SIDED && skipFirst) {
@ -90,21 +64,10 @@ void Group::GenerateMeshForStepAndRepeat(void) {
int ap = a*2 - (subtype == ONE_SIDED ? 0 : (n-1));
int remap = (a == (n - 1)) ? REMAP_LAST : a;
thisMesh.Clear();
if(type == TRANSLATE) {
Vector trans = Vector::From(h.param(0), h.param(1), h.param(2));
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 {
Vector trans = Vector::From(h.param(0), h.param(1), h.param(2));
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));
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) {
runningMesh.MakeFromDifference(&origm, &thisMesh);
} else {
runningMesh.MakeFromUnion(&origm, &thisMesh);
}
origm.Clear();
origm.MakeFromCopy(&runningMesh);
}
origm.Clear();
thisMesh.Clear();
}
void Group::GenerateMesh(void) {
thisMesh.Clear();
void Group::GenerateShellAndMesh(void) {
thisShell.Clear();
STriMeta meta = { 0, color };
if(type == TRANSLATE || type == ROTATE) {
GenerateMeshForStepAndRepeat();
GenerateShellForStepAndRepeat();
goto done;
}
if(type == EXTRUDE) {
SEdgeList edges;
ZERO(&edges);
int i;
Group *src = SS.GetGroup(opA);
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);
}
thisShell = SShell::FromExtrusionOf(&(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(); */
thisShell.MakeFromExtrusionOf(&(src->bezierLoopSet), tbot, ttop);
} else if(type == LATHE) {
SEdgeList edges;
ZERO(&edges);
int a, i;
Group *src = SS.GetGroup(opA);
(src->poly).MakeEdgesInto(&edges);
Vector orig = SS.GetEntity(predef.origin)->PointGetNum();
Vector axis = SS.GetEntity(predef.entityB)->VectorGetNum();
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) {
// Triangles are just copied over, with the appropriate transformation
// applied.
@ -291,7 +136,6 @@ void Group::GenerateMesh(void) {
st.a = q.Rotate(st.a).Plus(offset);
st.b = q.Rotate(st.b).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
// same as last time, no combining required. Likewise if we have a mesh
// but it's suppressed.
if(thisMesh.l.n == 0 || suppress) {
runningMesh.MakeFromCopy(PreviousGroupMesh());
if(suppress) {
runningShell.MakeFromCopyOf(PreviousGroupShell());
goto done;
}
// So our group's mesh appears in thisMesh. Combine this with the previous
// group's mesh, using the requested operation.
bool prevMeshError = meshError.yes;
meshError.yes = false;
meshError.interferesAt.Clear();
SMesh *a = PreviousGroupMesh();
SShell *a = PreviousGroupShell();
if(meshCombine == COMBINE_AS_UNION) {
runningMesh.MakeFromUnion(a, &thisMesh);
runningMesh = thisMesh;
ZERO(&thisMesh);
runningShell.MakeFromUnionOf(a, &thisShell);
} else if(meshCombine == COMBINE_AS_DIFFERENCE) {
runningMesh.MakeFromDifference(a, &thisMesh);
runningShell.MakeFromDifferenceOf(a, &thisShell);
} else {
if(!runningMesh.MakeFromInterferenceCheck(a, &thisMesh,
&(meshError.interferesAt)))
if(0) //&(meshError.interferesAt)
{
meshError.yes = true;
// And the list of failed triangles goes in meshError.interferesAt
@ -332,25 +175,21 @@ 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;
runningShell.TriangulateInto(&runningMesh);
emphEdges.Clear();
if(h.v == SS.GW.activeGroup.v && SS.edgeColor != 0) {
thisShell.MakeEdgesInto(&emphEdges);
}
}
SMesh *Group::PreviousGroupMesh(void) {
SShell *Group::PreviousGroupShell(void) {
int i;
for(i = 0; i < SS.group.n; i++) {
Group *g = &(SS.group.elem[i]);
if(g->h.v == h.v) break;
}
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) {
@ -428,15 +267,21 @@ void Group::Draw(void) {
glPopMatrix();
glEnable(GL_DEPTH_TEST);
}
} else if(polyError.how == POLY_NOT_COPLANAR) {
// And this one too
} else if(polyError.how == POLY_NOT_COPLANAR ||
polyError.how == POLY_SELF_INTERSECTING)
{
// These errors occur at points, not lines
if(type == DRAWING_WORKPLANE) {
glDisable(GL_DEPTH_TEST);
glxColor3d(1, 0, 0);
glPushMatrix();
glxTranslatev(polyError.notCoplanarAt);
glxTranslatev(polyError.errorPointAt);
glxOntoWorkplane(SS.GW.projRight, SS.GW.projUp);
glxWriteText("points not all coplanar!");
if(polyError.how == POLY_NOT_COPLANAR) {
glxWriteText("points not all coplanar!");
} else {
glxWriteText("contour is self-intersecting!");
}
glPopMatrix();
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,
STriMeta m, int cnt)
{

View File

@ -140,22 +140,29 @@ bool SEdgeList::AssemblePolygon(SPolygon *dest, SEdge *errorAt) {
// 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),
// 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);
double t_eps = LENGTH_EPS/d.Magnitude();
int cnt = 0;
SEdge *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);
double tse_eps = LENGTH_EPS/dse.Magnitude();
if(a.Equals(se->a) && b.Equals(se->b)) return true;
if(b.Equals(se->a) && a.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)) goto intersects;
double dist_a = (se->a).DistanceToLine(a, d),
dist_b = (se->b).DistanceToLine(a, d);
dist_a = (se->a).DistanceToLine(a, d),
dist_b = (se->b).DistanceToLine(a, d);
if(fabs(dist_a - dist_b) < LENGTH_EPS) {
// The edges are parallel.
@ -167,28 +174,25 @@ bool SEdgeList::AnyEdgeCrosses(Vector a, Vector b) {
// on the other
double t;
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);
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);
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);
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.
continue;
}
// Lines are not parallel, so look for an intersection.
double t, tse;
bool skew;
Vector pi = Vector::AtIntersectionOfLines(a, b,
se->a, se->b,
&skew,
&t, &tse);
pi = Vector::AtIntersectionOfLines(a, b, se->a, se->b,
&skew,
&t, &tse);
if(skew) continue;
bool inOrEdge0 = (t > -t_eps) && (t < (1 + t_eps));
bool inOrEdge1 = (tse > -tse_eps) && (tse < (1 + tse_eps));
inOrEdge0 = (t > -t_eps) && (t < (1 + t_eps));
inOrEdge1 = (tse > -tse_eps) && (tse < (1 + tse_eps));
if(inOrEdge0 && inOrEdge1) {
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
// inside of the other (or if they cross away from either's
// 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) {
@ -416,6 +426,19 @@ bool SPolygon::AllPointsInPlane(Vector *notCoplanarAt) {
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 Vector Tri1, TriNMinus1, TriNMinus2;
static Vector TriNormal;

View File

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

View File

@ -138,24 +138,24 @@ public:
SPolygon poly;
SBezierLoopSet bezierLoopSet;
static const int POLY_GOOD = 0;
static const int POLY_NOT_CLOSED = 1;
static const int POLY_NOT_COPLANAR = 2;
static const int POLY_GOOD = 0;
static const int POLY_NOT_CLOSED = 1;
static const int POLY_NOT_COPLANAR = 2;
static const int POLY_SELF_INTERSECTING = 3;
struct {
int how;
SEdge notClosedAt;
Vector notCoplanarAt;
Vector errorPointAt;
} polyError;
SMesh thisMesh;
SShell thisShell;
SShell runningShell;
SMesh runningMesh;
struct {
SMesh interferesAt;
bool yes;
} meshError;
SEdgeList emphEdges;
SShell thisShell;
SShell runningShell;
static const int COMBINE_AS_UNION = 0;
static const int COMBINE_AS_DIFFERENCE = 1;
@ -206,11 +206,9 @@ public:
bool AssembleLoops(void);
void GenerateLoops(void);
// And the mesh stuff
SMesh *PreviousGroupMesh(void);
void AddQuadWithNormal(STriMeta meta, Vector out,
Vector a, Vector b, Vector c, Vector d);
void GenerateMeshForStepAndRepeat(void);
void GenerateMesh(void);
SShell *PreviousGroupShell(void);
void GenerateShellForStepAndRepeat(void);
void GenerateShellAndMesh(void);
void Draw(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) {
l.Clear();
@ -292,6 +300,22 @@ void SBezierLoopSet::Clear(void) {
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) {
pts.Clear();
}
@ -346,6 +370,37 @@ SSurface SSurface::FromPlane(Vector pt, Vector n) {
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 num = Vector::From(0, 0, 0);
double den = 0;
@ -449,31 +504,41 @@ void SSurface::ClosestPointTo(Vector p, double *u, double *v) {
}
}
void SSurface::TriangulateInto(SShell *shell, SMesh *sm) {
SEdgeList el;
ZERO(&el);
void SSurface::MakeEdgesInto(SShell *shell, SEdgeList *sel, bool asUv) {
STrimBy *stb;
for(stb = trim.First(); stb; stb = trim.NextAfter(stb)) {
SCurve *sc = shell->curve.FindById(stb->curve);
Vector prevuv, ptuv;
Vector prev, prevuv, ptuv;
bool inCurve = false;
Vector *pt;
double u = 0, v = 0;
for(pt = sc->pts.First(); pt; pt = sc->pts.NextAfter(pt)) {
ClosestPointTo(*pt, &u, &v);
ptuv = Vector::From(u, v, 0);
if(inCurve) {
el.AddEdge(prevuv, ptuv);
if(asUv) {
ClosestPointTo(*pt, &u, &v);
ptuv = Vector::From(u, v, 0);
if(inCurve) {
sel->AddEdge(prevuv, ptuv);
}
prevuv = ptuv;
} else {
if(inCurve) {
sel->AddEdge(prev, *pt);
}
prev = *pt;
}
prevuv = ptuv;
if(pt->EqualsExactly(stb->start)) inCurve = true;
if(pt->EqualsExactly(stb->finish)) inCurve = false;
}
}
}
void SSurface::TriangulateInto(SShell *shell, SMesh *sm) {
SEdgeList el;
ZERO(&el);
MakeEdgesInto(shell, &el, true);
SPolygon poly;
ZERO(&poly);
@ -508,9 +573,8 @@ void SSurface::Clear(void) {
trim.Clear();
}
SShell SShell::FromExtrusionOf(SBezierLoopSet *sbls, Vector t0, Vector t1) {
SShell ret;
ZERO(&ret);
void SShell::MakeFromExtrusionOf(SBezierLoopSet *sbls, Vector t0, Vector t1) {
ZERO(this);
// Make the extrusion direction consistent with respect to the normal
// of the sketch we're extruding.
@ -523,8 +587,8 @@ SShell SShell::FromExtrusionOf(SBezierLoopSet *sbls, Vector t0, Vector t1) {
SSurface s0, s1;
s0 = SSurface::FromPlane(sbls->point.Plus(t0), sbls->normal.ScaledBy(-1));
s1 = SSurface::FromPlane(sbls->point.Plus(t1), sbls->normal.ScaledBy( 1));
hSSurface hs0 = ret.surface.AddAndAssignId(&s0),
hs1 = ret.surface.AddAndAssignId(&s1);
hSSurface hs0 = surface.AddAndAssignId(&s0),
hs1 = surface.AddAndAssignId(&s1);
// Now go through the input curves. For each one, generate its surface
// 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
// it to the list
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
SCurve sc;
ZERO(&sc);
sb->MakePwlInto(&(sc.pts), t0);
hSCurve hc0 = ret.curve.AddAndAssignId(&sc);
STrimBy stb0 = STrimBy::EntireCurve(&ret, hc0);
hSCurve hc0 = curve.AddAndAssignId(&sc);
STrimBy stb0 = STrimBy::EntireCurve(this, hc0);
ZERO(&sc);
sb->MakePwlInto(&(sc.pts), t1);
hSCurve hc1 = ret.curve.AddAndAssignId(&sc);
STrimBy stb1 = STrimBy::EntireCurve(&ret, hc1);
hSCurve hc1 = curve.AddAndAssignId(&sc);
STrimBy stb1 = STrimBy::EntireCurve(this, hc1);
// The translated curves trim the flat top and bottom surfaces.
// (ret.surface.FindById(hs0))->trim.Add(&stb0);
(ret.surface.FindById(hs1))->trim.Add(&stb1);
(surface.FindById(hs0))->trim.Add(&stb0);
(surface.FindById(hs1))->trim.Add(&stb1);
// The translated curves also trim the surface of extrusion.
// (ret.surface.FindById(hsext))->trim.Add(&stb0);
// (ret.surface.FindById(hsext))->trim.Add(&stb1);
(surface.FindById(hsext))->trim.Add(&stb0);
(surface.FindById(hsext))->trim.Add(&stb1);
// And form the trim line
Vector pt = sb->Finish();
@ -572,10 +636,10 @@ SShell SShell::FromExtrusionOf(SBezierLoopSet *sbls, Vector t0, Vector t1) {
ZERO(&sc);
sc.pts.Add(&p0);
sc.pts.Add(&p1);
hSCurve hl = ret.curve.AddAndAssignId(&sc);
hSCurve hl = curve.AddAndAssignId(&sc);
// save this for later
TrimLine tl;
tl.trim = STrimBy::EntireCurve(&ret, hl);
tl.trim = STrimBy::EntireCurve(this, hl);
tl.hs = hsext;
trimLines.Add(&tl);
}
@ -583,17 +647,45 @@ SShell SShell::FromExtrusionOf(SBezierLoopSet *sbls, Vector t0, Vector t1) {
int i;
for(i = 0; i < trimLines.n; 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)]);
// ss->trim.Add(&(tl->trim));
// ss->trim.Add(&(tlp->trim));
ss->trim.Add(&(tl->trim));
ss->trim.Add(&(tlp->trim));
}
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) {

View File

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

View File

@ -133,7 +133,7 @@ bool SContour::BridgeToContour(SContour *sc,
}
if(f) continue;
if(avoidEdges->AnyEdgeCrosses(a, b)) {
if(avoidEdges->AnyEdgeCrossings(a, b) > 0) {
// doesn't work, bridge crosses an existing edge
} else {
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 zero out all the dynamic stuff that will get regenerated.
dest.clean = false;
dest.vvMeshClean = false;
ZERO(&(dest.solved));
ZERO(&(dest.poly));
ZERO(&(dest.bezierLoopSet));
ZERO(&(dest.polyError));
ZERO(&(dest.thisMesh));
ZERO(&(dest.runningMesh));
ZERO(&(dest.thisShell));
ZERO(&(dest.runningShell));
@ -95,7 +93,6 @@ void SolveSpace::PopOntoCurrentFrom(UndoStack *uk) {
Group *g = &(group.elem[i]);
g->poly.Clear();
g->bezierLoopSet.Clear();
g->thisMesh.Clear();
g->runningMesh.Clear();
g->thisShell.Clear();
g->runningShell.Clear();