Add code to identify planes and cylindrical surfaces from a solid

of revolution, and put them in the same form as if they had been
draw by an extrusion (so that we can use all the same special case
intersection curves).

And add code to merge coincident faces into one. That turns out to
be more than a cosmetic/efficiency thing, since edge splitting
fails at the join between two coincident faces.

[git-p4: depot-paths = "//depot/solvespace/": change = 1965]
This commit is contained in:
Jonathan Westhues 2009-06-04 21:38:41 -08:00
parent ae35b3595c
commit 2d653eada8
7 changed files with 189 additions and 7 deletions

View File

@ -46,6 +46,7 @@ SRFOBJS = $(OBJDIR)\ratpoly.obj \
$(OBJDIR)\triangulate.obj \ $(OBJDIR)\triangulate.obj \
$(OBJDIR)\boolean.obj \ $(OBJDIR)\boolean.obj \
$(OBJDIR)\surfinter.obj \ $(OBJDIR)\surfinter.obj \
$(OBJDIR)\merge.obj \
RES = $(OBJDIR)\resource.res RES = $(OBJDIR)\resource.res
@ -56,7 +57,7 @@ LIBS = user32.lib gdi32.lib comctl32.lib advapi32.lib shell32.lib opengl32.lib g
all: $(OBJDIR)/solvespace.exe all: $(OBJDIR)/solvespace.exe
@cp $(OBJDIR)/solvespace.exe . @cp $(OBJDIR)/solvespace.exe .
solvespace alext.slvs solvespace t8.slvs
clean: clean:
rm -f obj/* rm -f obj/*

View File

@ -168,6 +168,7 @@ void Group::GenerateShellAndMesh(void) {
GenerateForStepAndRepeat<SShell> GenerateForStepAndRepeat<SShell>
(prev, toStep, &runningShell, src->meshCombine); (prev, toStep, &runningShell, src->meshCombine);
runningShell.MergeCoincidentSurfaces();
} else { } else {
SMesh prevm, stepm; SMesh prevm, stepm;
ZERO(&prevm); ZERO(&prevm);
@ -287,6 +288,8 @@ void Group::GenerateShellAndMesh(void) {
thisShell.RemapFaces(this, 0); thisShell.RemapFaces(this, 0);
} }
thisShell.MergeCoincidentSurfaces();
// So now we've got the mesh or shell for this group. Combine it with // So now we've got the mesh or shell for this group. Combine it with
// the previous group's mesh or shell with the requested Boolean, and // the previous group's mesh or shell with the requested Boolean, and
// we're done. // we're done.
@ -295,6 +298,7 @@ void Group::GenerateShellAndMesh(void) {
if(pg->runningMesh.IsEmpty() && thisMesh.IsEmpty() && !forceToMesh) { if(pg->runningMesh.IsEmpty() && thisMesh.IsEmpty() && !forceToMesh) {
SShell *prevs = &(pg->runningShell); SShell *prevs = &(pg->runningShell);
GenerateForBoolean<SShell>(prevs, &thisShell, &runningShell); GenerateForBoolean<SShell>(prevs, &thisShell, &runningShell);
runningShell.MergeCoincidentSurfaces();
// If the Boolean failed, then we should note that in the text screen // If the Boolean failed, then we should note that in the text screen
// for this group. // for this group.

View File

@ -109,7 +109,7 @@ void SShell::CopyCurvesSplitAgainst(bool opA, SShell *agnst, SShell *into) {
} }
} }
void SSurface::TrimFromEdgeList(SEdgeList *el) { void SSurface::TrimFromEdgeList(SEdgeList *el, bool asUv) {
el->l.ClearTags(); el->l.ClearTags();
STrimBy stb; STrimBy stb;
@ -151,9 +151,12 @@ void SSurface::TrimFromEdgeList(SEdgeList *el) {
} }
} while(merged); } while(merged);
if(asUv) {
stb.start = PointAt(stb.start.x, stb.start.y);
stb.finish = PointAt(stb.finish.x, stb.finish.y);
}
// And add the merged trim, with xyz (not uv like the polygon) pts // And add the merged trim, with xyz (not uv like the polygon) pts
stb.start = PointAt(stb.start.x, stb.start.y);
stb.finish = PointAt(stb.finish.x, stb.finish.y);
trim.Add(&stb); trim.Add(&stb);
} }
} }
@ -444,7 +447,7 @@ SSurface SSurface::MakeCopyTrimAgainst(SShell *agnst, SShell *parent,
final.CullExtraneousEdges(); final.CullExtraneousEdges();
// Use our reassembled edges to trim the new surface. // Use our reassembled edges to trim the new surface.
ret.TrimFromEdgeList(&final); ret.TrimFromEdgeList(&final, true);
SPolygon poly; SPolygon poly;
ZERO(&poly); ZERO(&poly);

87
srf/merge.cpp Normal file
View File

@ -0,0 +1,87 @@
//-----------------------------------------------------------------------------
// Routines to merge multiple coincident surfaces (each with their own trim
// curves) into a single surface, with all of the trim curves.
//-----------------------------------------------------------------------------
#include "../solvespace.h"
void SShell::MergeCoincidentSurfaces(void) {
surface.ClearTags();
int i, j;
SSurface *si, *sj;
for(i = 0; i < surface.n; i++) {
si = &(surface.elem[i]);
if(si->tag) continue;
SEdgeList sel;
ZERO(&sel);
bool merged = false;
for(j = i + 1; j < surface.n; j++) {
sj = &(surface.elem[j]);
if(sj->tag) continue;
if(!sj->CoincidentWith(si, true)) continue;
if(sj->color != si->color) continue;
// But we do merge surfaces with different face entities, since
// otherwise we'd hardly ever merge anything.
// This surface is coincident, so it gets merged.
sj->tag = 1;
merged = true;
sj->MakeEdgesInto(this, &sel, false);
sj->trim.Clear();
// All the references to this surface get replaced with the new srf
SCurve *sc;
for(sc = curve.First(); sc; sc = curve.NextAfter(sc)) {
if(sc->surfA.v == sj->h.v) sc->surfA = si->h;
if(sc->surfB.v == sj->h.v) sc->surfB = si->h;
}
}
if(merged) {
si->MakeEdgesInto(this, &sel, false);
sel.CullExtraneousEdges();
si->trim.Clear();
si->TrimFromEdgeList(&sel, false);
// And we must choose control points such that all the trims lie
// with u and v in [0, 1], so that the bbox tests work.
Vector u, v, n;
si->TangentsAt(0.5, 0.5, &u, &v);
u = u.WithMagnitude(1);
v = v.WithMagnitude(1);
n = si->NormalAt(0.5, 0.5).WithMagnitude(1);
v = (n.Cross(u)).WithMagnitude(1);
double umax = VERY_NEGATIVE, umin = VERY_POSITIVE,
vmax = VERY_NEGATIVE, vmin = VERY_POSITIVE;
SEdge *se;
for(se = sel.l.First(); se; se = sel.l.NextAfter(se)) {
double ut = (se->a).Dot(u), vt = (se->a).Dot(v);
umax = max(umax, ut);
vmax = max(vmax, vt);
umin = min(umin, ut);
vmin = min(vmin, vt);
}
// We move in the +v direction as v goes from 0 to 1, and in the
// +u direction as u goes from 0 to 1. So our normal ends up
// pointed the same direction.
double nt = (si->ctrl[0][0]).Dot(n);
si->ctrl[0][0] =
Vector::From(umin, vmin, nt).ScaleOutOfCsys(u, v, n);
si->ctrl[0][1] =
Vector::From(umin, vmax, nt).ScaleOutOfCsys(u, v, n);
si->ctrl[1][1] =
Vector::From(umax, vmax, nt).ScaleOutOfCsys(u, v, n);
si->ctrl[1][0] =
Vector::From(umax, vmin, nt).ScaleOutOfCsys(u, v, n);
}
}
surface.RemoveTagged();
}

View File

@ -67,6 +67,8 @@ SSurface SSurface::FromRevolutionOf(SBezier *sb, Vector pt, Vector axis,
{ {
SSurface ret; SSurface ret;
ZERO(&ret); ZERO(&ret);
ret.degm = sb->deg; ret.degm = sb->deg;
ret.degn = 2; ret.degn = 2;
@ -499,6 +501,8 @@ void SShell::MakeFromRevolutionOf(SBezierLoopSet *sbls, Vector pt, Vector axis,
ZERO(this); ZERO(this);
SBezierLoop *sbl; SBezierLoop *sbl;
int i0 = surface.n, i;
// Normalize the axis direction so that the direction of revolution // Normalize the axis direction so that the direction of revolution
// ends up parallel to the normal of the sketch, on the side of the // ends up parallel to the normal of the sketch, on the side of the
// axis where the sketch is. // axis where the sketch is.
@ -619,6 +623,86 @@ void SShell::MakeFromRevolutionOf(SBezierLoopSet *sbls, Vector pt, Vector axis,
hsl.Clear(); hsl.Clear();
} }
for(i = i0; i < surface.n; i++) {
SSurface *srf = &(surface.elem[i]);
// Revolution of a line; this is potentially a plane, which we can
// rewrite to have degree (1, 1).
if(srf->degm == 1 && srf->degn == 2) {
// close start, far start, far finish
Vector cs, fs, ff;
double d0, d1;
d0 = (srf->ctrl[0][0]).DistanceToLine(pt, axis);
d1 = (srf->ctrl[1][0]).DistanceToLine(pt, axis);
if(d0 > d1) {
cs = srf->ctrl[1][0];
fs = srf->ctrl[0][0];
ff = srf->ctrl[0][2];
} else {
cs = srf->ctrl[0][0];
fs = srf->ctrl[1][0];
ff = srf->ctrl[1][2];
}
// origin close, origin far
Vector oc = cs.ClosestPointOnLine(pt, axis),
of = fs.ClosestPointOnLine(pt, axis);
if(oc.Equals(of)) {
// This is a plane, not a (non-degenerate) cone.
Vector oldn = srf->NormalAt(0.5, 0.5);
Vector u = fs.Minus(of), v;
v = (axis.Cross(u)).WithMagnitude(1);
double vm = (ff.Minus(of)).Dot(v);
v = v.ScaledBy(vm);
srf->degm = 1;
srf->degn = 1;
srf->ctrl[0][0] = of;
srf->ctrl[0][1] = of.Plus(u);
srf->ctrl[1][0] = of.Plus(v);
srf->ctrl[1][1] = of.Plus(u).Plus(v);
srf->weight[0][0] = 1;
srf->weight[0][1] = 1;
srf->weight[1][0] = 1;
srf->weight[1][1] = 1;
if(oldn.Dot(srf->NormalAt(0.5, 0.5)) < 0) {
SWAP(Vector, srf->ctrl[0][0], srf->ctrl[1][0]);
SWAP(Vector, srf->ctrl[0][1], srf->ctrl[1][1]);
}
continue;
}
if(fabs(d0 - d1) < LENGTH_EPS) {
// This is a cylinder; so transpose it so that we'll recognize
// it as a surface of extrusion.
SSurface sn = *srf;
// Transposing u and v flips the normal, so reverse u to
// flip it again and put it back where we started.
sn.degm = 2;
sn.degn = 1;
int dm, dn;
for(dm = 0; dm <= 1; dm++) {
for(dn = 0; dn <= 2; dn++) {
sn.ctrl [dn][dm] = srf->ctrl [1-dm][dn];
sn.weight[dn][dm] = srf->weight[1-dm][dn];
}
}
*srf = sn;
continue;
}
}
}
} }
void SShell::MakeFromCopyOf(SShell *a) { void SShell::MakeFromCopyOf(SShell *a) {

View File

@ -194,6 +194,7 @@ public:
// A rational polynomial surface in Bezier form. // A rational polynomial surface in Bezier form.
class SSurface { class SSurface {
public: public:
int tag;
hSSurface h; hSSurface h;
// Same as newH for the curves; record what a surface gets renamed to // Same as newH for the curves; record what a surface gets renamed to
@ -226,7 +227,7 @@ public:
DWORD auxA, SShell *shell); DWORD auxA, SShell *shell);
SSurface MakeCopyTrimAgainst(SShell *against, SShell *parent, SShell *into, SSurface MakeCopyTrimAgainst(SShell *against, SShell *parent, SShell *into,
int type, bool opA); int type, bool opA);
void TrimFromEdgeList(SEdgeList *el); void TrimFromEdgeList(SEdgeList *el, bool asUv);
void IntersectAgainst(SSurface *b, SShell *agnstA, SShell *agnstB, void IntersectAgainst(SSurface *b, SShell *agnstA, SShell *agnstB,
SShell *into); SShell *into);
void AddExactIntersectionCurve(SBezier *sb, SSurface *srfB, void AddExactIntersectionCurve(SBezier *sb, SSurface *srfB,
@ -330,6 +331,7 @@ public:
void MakeFromCopyOf(SShell *a); void MakeFromCopyOf(SShell *a);
void MakeFromTransformationOf(SShell *a, Vector trans, Quaternion q); void MakeFromTransformationOf(SShell *a, Vector trans, Quaternion q);
void MakeFromAssemblyOf(SShell *a, SShell *b); void MakeFromAssemblyOf(SShell *a, SShell *b);
void MergeCoincidentSurfaces(void);
void TriangulateInto(SMesh *sm); void TriangulateInto(SMesh *sm);
void MakeEdgesInto(SEdgeList *sel); void MakeEdgesInto(SEdgeList *sel);

View File

@ -58,7 +58,8 @@ void SSurface::AddExactIntersectionCurve(SBezier *sb, SSurface *srfB,
dbp("split.pts.n =%d", split.pts.n); dbp("split.pts.n =%d", split.pts.n);
for(v = split.pts.First(); v; v = split.pts.NextAfter(v)) { for(v = split.pts.First(); v; v = split.pts.NextAfter(v)) {
if(prev) { if(prev) {
SS.nakedEdges.AddEdge(prev->p, v->p); Vector e = (prev->p).Minus(v->p).WithMagnitude(-1);
SS.nakedEdges.AddEdge((prev->p).Plus(e), (v->p).Minus(e));
} }
prev = v; prev = v;
} }