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:
parent
ae35b3595c
commit
2d653eada8
3
Makefile
3
Makefile
|
@ -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/*
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
// And add the merged trim, with xyz (not uv like the polygon) pts
|
if(asUv) {
|
||||||
stb.start = PointAt(stb.start.x, stb.start.y);
|
stb.start = PointAt(stb.start.x, stb.start.y);
|
||||||
stb.finish = PointAt(stb.finish.x, stb.finish.y);
|
stb.finish = PointAt(stb.finish.x, stb.finish.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
// And add the merged trim, with xyz (not uv like the polygon) pts
|
||||||
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
87
srf/merge.cpp
Normal 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();
|
||||||
|
}
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user