diff --git a/Makefile b/Makefile index 62a102dd..cb9aaf4b 100644 --- a/Makefile +++ b/Makefile @@ -40,6 +40,8 @@ SSOBJS = $(OBJDIR)\solvespace.obj \ $(OBJDIR)\export.obj \ SRFOBJS = $(OBJDIR)\ratpoly.obj \ + $(OBJDIR)\curve.obj \ + $(OBJDIR)\surface.obj \ $(OBJDIR)\triangulate.obj \ $(OBJDIR)\boolean.obj \ $(OBJDIR)\surfinter.obj \ @@ -53,7 +55,7 @@ LIBS = user32.lib gdi32.lib comctl32.lib advapi32.lib shell32.lib opengl32.lib g all: $(OBJDIR)/solvespace.exe @cp $(OBJDIR)/solvespace.exe . - solvespace tttt.slvs + solvespace t7.slvs clean: rm -f obj/* diff --git a/export.cpp b/export.cpp index 771be283..f0da0551 100644 --- a/export.cpp +++ b/export.cpp @@ -116,9 +116,9 @@ void SolveSpace::ExportViewTo(char *filename) { } if(SS.GW.showEdges) { - SEdgeList *emph = &((SS.GetGroup(SS.GW.activeGroup))->emphEdges); + SEdgeList *selr = &((SS.GetGroup(SS.GW.activeGroup))->runningEdges); SEdge *se; - for(se = emph->l.First(); se; se = emph->l.NextAfter(se)) { + for(se = selr->l.First(); se; se = selr->l.NextAfter(se)) { edges.AddEdge(se->a, se->b); } } diff --git a/graphicswin.cpp b/graphicswin.cpp index 0827d219..d8cd115f 100644 --- a/graphicswin.cpp +++ b/graphicswin.cpp @@ -105,6 +105,7 @@ const GraphicsWindow::MenuEntry GraphicsWindow::menu[] = { { 0, "&Analyze", 0, NULL }, { 1, "Measure &Volume\tCtrl+Shift+V", MNU_VOLUME, 'V'|S|C,mAna }, +{ 1, "Show &Interfering Parts\tCtrl+Shift+I", MNU_INTERFERENCE, 'I'|S|C,mAna }, { 1, "Show &Naked Edges\tCtrl+Shift+N", MNU_NAKED_EDGES, 'N'|S|C,mAna }, { 1, NULL, 0, NULL }, { 1, "Show Degrees of &Freedom\tCtrl+Shift+F", MNU_SHOW_DOF, 'F'|S|C,mAna }, diff --git a/groupmesh.cpp b/groupmesh.cpp index 85d17f0e..2a42521e 100644 --- a/groupmesh.cpp +++ b/groupmesh.cpp @@ -227,33 +227,20 @@ void Group::GenerateShellAndMesh(void) { // 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(); - SShell *a = PreviousGroupShell(); if(meshCombine == COMBINE_AS_UNION) { runningShell.MakeFromUnionOf(a, &thisShell); } else if(meshCombine == COMBINE_AS_DIFFERENCE) { runningShell.MakeFromDifferenceOf(a, &thisShell); } else { - if(0) //&(meshError.interferesAt) - { - meshError.yes = true; - // And the list of failed triangles goes in meshError.interferesAt - } - } - if(prevMeshError != meshError.yes) { - // The error is reported in the text window for the group. - SS.later.showTW = true; + // TODO, assembly } done: runningMesh.Clear(); runningShell.TriangulateInto(&runningMesh); - emphEdges.Clear(); - runningShell.MakeEdgesInto(&emphEdges); + runningEdges.Clear(); + runningShell.MakeEdgesInto(&runningEdges); } SShell *Group::PreviousGroupShell(void) { @@ -303,24 +290,7 @@ void Group::Draw(void) { glxColor3d(REDf (SS.edgeColor), GREENf(SS.edgeColor), BLUEf (SS.edgeColor)); - glxDrawEdges(&emphEdges); - } - - if(meshError.yes) { - // Draw the error triangles in bright red stripes, with no Z buffering - GLubyte mask[32*32/8]; - memset(mask, 0xf0, sizeof(mask)); - glPolygonStipple(mask); - - int specColor = 0; - glDisable(GL_DEPTH_TEST); - glColor3d(0, 0, 0); - glxFillMesh(0, &meshError.interferesAt, 0, 0, 0); - glEnable(GL_POLYGON_STIPPLE); - glColor3d(1, 0, 0); - glxFillMesh(0, &meshError.interferesAt, 0, 0, 0); - glEnable(GL_DEPTH_TEST); - glDisable(GL_POLYGON_STIPPLE); + glxDrawEdges(&runningEdges); } if(SS.GW.showMesh) glxDebugMesh(&runningMesh); diff --git a/sketch.h b/sketch.h index f6d71ca3..15d3a85f 100644 --- a/sketch.h +++ b/sketch.h @@ -150,12 +150,9 @@ public: SShell thisShell; SShell runningShell; + SMesh runningMesh; - struct { - SMesh interferesAt; - bool yes; - } meshError; - SEdgeList emphEdges; + SEdgeList runningEdges; static const int COMBINE_AS_UNION = 0; static const int COMBINE_AS_DIFFERENCE = 1; diff --git a/solvespace.cpp b/solvespace.cpp index 10a768ef..5c029f7c 100644 --- a/solvespace.cpp +++ b/solvespace.cpp @@ -500,6 +500,9 @@ void SolveSpace::MenuAnalyze(int id) { break; } + case GraphicsWindow::MNU_INTERFERENCE: + break; + case GraphicsWindow::MNU_SHOW_DOF: // This works like a normal solve, except that it calculates // which variables are free/bound at the same time. diff --git a/srf/curve.cpp b/srf/curve.cpp new file mode 100644 index 00000000..ccc0f53d --- /dev/null +++ b/srf/curve.cpp @@ -0,0 +1,290 @@ +//----------------------------------------------------------------------------- +// Anything involving curves and sets of curves (except for the real math, +// which is in ratpoly.cpp). +//----------------------------------------------------------------------------- +#include "../solvespace.h" + +SBezier SBezier::From(Vector p0, Vector p1) { + SBezier ret; + ZERO(&ret); + ret.deg = 1; + ret.weight[0] = ret.weight[1] = 1; + ret.ctrl[0] = p0; + ret.ctrl[1] = p1; + return ret; +} + +SBezier SBezier::From(Vector p0, Vector p1, Vector p2) { + SBezier ret; + ZERO(&ret); + ret.deg = 2; + ret.weight[0] = ret.weight[1] = ret.weight[2] = 1; + ret.ctrl[0] = p0; + ret.ctrl[1] = p1; + ret.ctrl[2] = p2; + return ret; +} + +SBezier SBezier::From(Vector p0, Vector p1, Vector p2, Vector p3) { + SBezier ret; + ZERO(&ret); + ret.deg = 3; + ret.weight[0] = ret.weight[1] = ret.weight[2] = ret.weight[3] = 1; + ret.ctrl[0] = p0; + ret.ctrl[1] = p1; + ret.ctrl[2] = p2; + ret.ctrl[3] = p3; + return ret; +} + +Vector SBezier::Start(void) { + return ctrl[0]; +} + +Vector SBezier::Finish(void) { + return ctrl[deg]; +} + +void SBezier::Reverse(void) { + int i; + for(i = 0; i < (deg+1)/2; i++) { + SWAP(Vector, ctrl[i], ctrl[deg-i]); + SWAP(double, weight[i], weight[deg-i]); + } +} + +void SBezier::GetBoundingProjd(Vector u, Vector orig, + double *umin, double *umax) +{ + int i; + for(i = 0; i <= deg; i++) { + double ut = ((ctrl[i]).Minus(orig)).Dot(u); + if(ut < *umin) *umin = ut; + if(ut > *umax) *umax = ut; + } +} + +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; +} + +bool SBezier::Equals(SBezier *b) { + // We just test of identical degree and control points, even though two + // curves could still be coincident (even sharing endpoints). + if(deg != b->deg) return false; + int i; + for(i = 0; i <= deg; i++) { + if(!(ctrl[i]).Equals(b->ctrl[i])) return false; + if(fabs(weight[i] - b->weight[i]) > LENGTH_EPS) return false; + } + return true; +} + +void SBezierList::Clear(void) { + l.Clear(); +} + + +SBezierLoop SBezierLoop::FromCurves(SBezierList *sbl, + bool *allClosed, SEdge *errorAt) +{ + SBezierLoop loop; + ZERO(&loop); + + if(sbl->l.n < 1) return loop; + sbl->l.ClearTags(); + + SBezier *first = &(sbl->l.elem[0]); + first->tag = 1; + loop.l.Add(first); + Vector start = first->Start(); + Vector hanging = first->Finish(); + + sbl->l.RemoveTagged(); + + while(sbl->l.n > 0 && !hanging.Equals(start)) { + int i; + bool foundNext = false; + for(i = 0; i < sbl->l.n; i++) { + SBezier *test = &(sbl->l.elem[i]); + + if((test->Finish()).Equals(hanging)) { + test->Reverse(); + // and let the next test catch it + } + if((test->Start()).Equals(hanging)) { + test->tag = 1; + loop.l.Add(test); + hanging = test->Finish(); + sbl->l.RemoveTagged(); + foundNext = true; + break; + } + } + if(!foundNext) { + // The loop completed without finding the hanging edge, so + // it's an open loop + errorAt->a = hanging; + errorAt->b = start; + *allClosed = false; + return loop; + } + } + if(hanging.Equals(start)) { + *allClosed = true; + } else { + // We ran out of edges without forming a closed loop. + errorAt->a = hanging; + errorAt->b = start; + *allClosed = false; + } + + return loop; +} + +void SBezierLoop::Reverse(void) { + l.Reverse(); + SBezier *sb; + for(sb = l.First(); sb; sb = l.NextAfter(sb)) { + // If we didn't reverse each curve, then the next curve in list would + // share your start, not your finish. + sb->Reverse(); + } +} + +void SBezierLoop::GetBoundingProjd(Vector u, Vector orig, + double *umin, double *umax) +{ + SBezier *sb; + for(sb = l.First(); sb; sb = l.NextAfter(sb)) { + sb->GetBoundingProjd(u, orig, umin, umax); + } +} + +void SBezierLoop::MakePwlInto(SContour *sc) { + List lv; + ZERO(&lv); + + int i, j; + for(i = 0; i < l.n; i++) { + SBezier *sb = &(l.elem[i]); + sb->MakePwlInto(&lv); + + // Each curve's piecewise linearization includes its endpoints, + // which we don't want to duplicate (creating zero-len edges). + for(j = (i == 0 ? 0 : 1); j < lv.n; j++) { + sc->AddPoint(lv.elem[j]); + } + lv.Clear(); + } + // Ensure that it's exactly closed, not just within a numerical tolerance. + sc->l.elem[sc->l.n - 1] = sc->l.elem[0]; +} + + +SBezierLoopSet SBezierLoopSet::From(SBezierList *sbl, SPolygon *poly, + bool *allClosed, SEdge *errorAt) +{ + int i; + SBezierLoopSet ret; + ZERO(&ret); + + while(sbl->l.n > 0) { + bool thisClosed; + SBezierLoop loop; + loop = SBezierLoop::FromCurves(sbl, &thisClosed, errorAt); + if(!thisClosed) { + ret.Clear(); + *allClosed = false; + return ret; + } + + ret.l.Add(&loop); + poly->AddEmptyContour(); + loop.MakePwlInto(&(poly->l.elem[poly->l.n-1])); + } + + poly->normal = poly->ComputeNormal(); + ret.normal = poly->normal; + if(poly->l.n > 0) { + ret.point = poly->AnyPoint(); + } else { + ret.point = Vector::From(0, 0, 0); + } + poly->FixContourDirections(); + + for(i = 0; i < poly->l.n; i++) { + if(poly->l.elem[i].tag) { + // We had to reverse this contour in order to fix the poly + // contour directions; so need to do the same with the curves. + ret.l.elem[i].Reverse(); + } + } + + *allClosed = true; + return ret; +} + +void SBezierLoopSet::GetBoundingProjd(Vector u, Vector orig, + double *umin, double *umax) +{ + SBezierLoop *sbl; + for(sbl = l.First(); sbl; sbl = l.NextAfter(sbl)) { + sbl->GetBoundingProjd(u, orig, umin, umax); + } +} + +void SBezierLoopSet::Clear(void) { + int i; + for(i = 0; i < l.n; i++) { + (l.elem[i]).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); + ret.surfA = a->surfA; + ret.surfB = a->surfB; + + 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(); +} + +STrimBy STrimBy::EntireCurve(SShell *shell, hSCurve hsc, bool backwards) { + STrimBy stb; + ZERO(&stb); + stb.curve = hsc; + SCurve *sc = shell->curve.FindById(hsc); + + if(backwards) { + stb.finish = sc->pts.elem[0]; + stb.start = sc->pts.elem[sc->pts.n - 1]; + stb.backwards = true; + } else { + stb.start = sc->pts.elem[0]; + stb.finish = sc->pts.elem[sc->pts.n - 1]; + stb.backwards = false; + } + + return stb; +} + diff --git a/srf/ratpoly.cpp b/srf/ratpoly.cpp index 12aa57c6..98c9ad01 100644 --- a/srf/ratpoly.cpp +++ b/srf/ratpoly.cpp @@ -1,3 +1,8 @@ +//----------------------------------------------------------------------------- +// Math on rational polynomial surfaces and curves, typically in Bezier +// form. Evaluate, root-find (by Newton's methods), evaluate derivatives, +// and so on. +//----------------------------------------------------------------------------- #include "../solvespace.h" // Converge it to better than LENGTH_EPS; we want two points, each @@ -88,47 +93,6 @@ double BernsteinDerivative(int k, int deg, double t) oops(); } -SBezier SBezier::From(Vector p0, Vector p1) { - SBezier ret; - ZERO(&ret); - ret.deg = 1; - ret.weight[0] = ret.weight[1] = 1; - ret.ctrl[0] = p0; - ret.ctrl[1] = p1; - return ret; -} - -SBezier SBezier::From(Vector p0, Vector p1, Vector p2) { - SBezier ret; - ZERO(&ret); - ret.deg = 2; - ret.weight[0] = ret.weight[1] = ret.weight[2] = 1; - ret.ctrl[0] = p0; - ret.ctrl[1] = p1; - ret.ctrl[2] = p2; - return ret; -} - -SBezier SBezier::From(Vector p0, Vector p1, Vector p2, Vector p3) { - SBezier ret; - ZERO(&ret); - ret.deg = 3; - ret.weight[0] = ret.weight[1] = ret.weight[2] = ret.weight[3] = 1; - ret.ctrl[0] = p0; - ret.ctrl[1] = p1; - ret.ctrl[2] = p2; - ret.ctrl[3] = p3; - return ret; -} - -Vector SBezier::Start(void) { - return ctrl[0]; -} - -Vector SBezier::Finish(void) { - return ctrl[deg]; -} - Vector SBezier::PointAt(double t) { Vector pt = Vector::From(0, 0, 0); double d = 0; @@ -172,394 +136,6 @@ void SBezier::MakePwlWorker(List *l, double ta, double tb) { } } -void SBezier::Reverse(void) { - int i; - for(i = 0; i < (deg+1)/2; i++) { - SWAP(Vector, ctrl[i], ctrl[deg-i]); - SWAP(double, weight[i], weight[deg-i]); - } -} - -void SBezier::GetBoundingProjd(Vector u, Vector orig, - double *umin, double *umax) -{ - int i; - for(i = 0; i <= deg; i++) { - double ut = ((ctrl[i]).Minus(orig)).Dot(u); - if(ut < *umin) *umin = ut; - if(ut > *umax) *umax = ut; - } -} - -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; -} - -bool SBezier::Equals(SBezier *b) { - // We just test of identical degree and control points, even though two - // curves could still be coincident (even sharing endpoints). - if(deg != b->deg) return false; - int i; - for(i = 0; i <= deg; i++) { - if(!(ctrl[i]).Equals(b->ctrl[i])) return false; - if(fabs(weight[i] - b->weight[i]) > LENGTH_EPS) return false; - } - return true; -} - -void SBezierList::Clear(void) { - l.Clear(); -} - - -SBezierLoop SBezierLoop::FromCurves(SBezierList *sbl, - bool *allClosed, SEdge *errorAt) -{ - SBezierLoop loop; - ZERO(&loop); - - if(sbl->l.n < 1) return loop; - sbl->l.ClearTags(); - - SBezier *first = &(sbl->l.elem[0]); - first->tag = 1; - loop.l.Add(first); - Vector start = first->Start(); - Vector hanging = first->Finish(); - - sbl->l.RemoveTagged(); - - while(sbl->l.n > 0 && !hanging.Equals(start)) { - int i; - bool foundNext = false; - for(i = 0; i < sbl->l.n; i++) { - SBezier *test = &(sbl->l.elem[i]); - - if((test->Finish()).Equals(hanging)) { - test->Reverse(); - // and let the next test catch it - } - if((test->Start()).Equals(hanging)) { - test->tag = 1; - loop.l.Add(test); - hanging = test->Finish(); - sbl->l.RemoveTagged(); - foundNext = true; - break; - } - } - if(!foundNext) { - // The loop completed without finding the hanging edge, so - // it's an open loop - errorAt->a = hanging; - errorAt->b = start; - *allClosed = false; - return loop; - } - } - if(hanging.Equals(start)) { - *allClosed = true; - } else { - // We ran out of edges without forming a closed loop. - errorAt->a = hanging; - errorAt->b = start; - *allClosed = false; - } - - return loop; -} - -void SBezierLoop::Reverse(void) { - l.Reverse(); - SBezier *sb; - for(sb = l.First(); sb; sb = l.NextAfter(sb)) { - // If we didn't reverse each curve, then the next curve in list would - // share your start, not your finish. - sb->Reverse(); - } -} - -void SBezierLoop::GetBoundingProjd(Vector u, Vector orig, - double *umin, double *umax) -{ - SBezier *sb; - for(sb = l.First(); sb; sb = l.NextAfter(sb)) { - sb->GetBoundingProjd(u, orig, umin, umax); - } -} - -void SBezierLoop::MakePwlInto(SContour *sc) { - List lv; - ZERO(&lv); - - int i, j; - for(i = 0; i < l.n; i++) { - SBezier *sb = &(l.elem[i]); - sb->MakePwlInto(&lv); - - // Each curve's piecewise linearization includes its endpoints, - // which we don't want to duplicate (creating zero-len edges). - for(j = (i == 0 ? 0 : 1); j < lv.n; j++) { - sc->AddPoint(lv.elem[j]); - } - lv.Clear(); - } - // Ensure that it's exactly closed, not just within a numerical tolerance. - sc->l.elem[sc->l.n - 1] = sc->l.elem[0]; -} - - -SBezierLoopSet SBezierLoopSet::From(SBezierList *sbl, SPolygon *poly, - bool *allClosed, SEdge *errorAt) -{ - int i; - SBezierLoopSet ret; - ZERO(&ret); - - while(sbl->l.n > 0) { - bool thisClosed; - SBezierLoop loop; - loop = SBezierLoop::FromCurves(sbl, &thisClosed, errorAt); - if(!thisClosed) { - ret.Clear(); - *allClosed = false; - return ret; - } - - ret.l.Add(&loop); - poly->AddEmptyContour(); - loop.MakePwlInto(&(poly->l.elem[poly->l.n-1])); - } - - poly->normal = poly->ComputeNormal(); - ret.normal = poly->normal; - if(poly->l.n > 0) { - ret.point = poly->AnyPoint(); - } else { - ret.point = Vector::From(0, 0, 0); - } - poly->FixContourDirections(); - - for(i = 0; i < poly->l.n; i++) { - if(poly->l.elem[i].tag) { - // We had to reverse this contour in order to fix the poly - // contour directions; so need to do the same with the curves. - ret.l.elem[i].Reverse(); - } - } - - *allClosed = true; - return ret; -} - -void SBezierLoopSet::GetBoundingProjd(Vector u, Vector orig, - double *umin, double *umax) -{ - SBezierLoop *sbl; - for(sbl = l.First(); sbl; sbl = l.NextAfter(sbl)) { - sbl->GetBoundingProjd(u, orig, umin, umax); - } -} - -void SBezierLoopSet::Clear(void) { - int i; - for(i = 0; i < l.n; i++) { - (l.elem[i]).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); - ret.surfA = a->surfA; - ret.surfB = a->surfB; - - 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(); -} - -STrimBy STrimBy::EntireCurve(SShell *shell, hSCurve hsc, bool backwards) { - STrimBy stb; - ZERO(&stb); - stb.curve = hsc; - SCurve *sc = shell->curve.FindById(hsc); - - if(backwards) { - stb.finish = sc->pts.elem[0]; - stb.start = sc->pts.elem[sc->pts.n - 1]; - stb.backwards = true; - } else { - stb.start = sc->pts.elem[0]; - stb.finish = sc->pts.elem[sc->pts.n - 1]; - stb.backwards = false; - } - - return stb; -} - -SSurface SSurface::FromExtrusionOf(SBezier *sb, Vector t0, Vector t1) { - SSurface ret; - ZERO(&ret); - - ret.degm = sb->deg; - ret.degn = 1; - - int i; - for(i = 0; i <= ret.degm; i++) { - ret.ctrl[i][0] = (sb->ctrl[i]).Plus(t0); - ret.weight[i][0] = sb->weight[i]; - - ret.ctrl[i][1] = (sb->ctrl[i]).Plus(t1); - ret.weight[i][1] = sb->weight[i]; - } - - return ret; -} - -bool SSurface::IsExtrusion(SBezier *of, Vector *alongp) { - int i; - - if(degn != 1) return false; - - Vector along = (ctrl[0][1]).Minus(ctrl[0][0]); - for(i = 0; i <= degm; i++) { - if((fabs(weight[i][1] - weight[i][0]) < LENGTH_EPS) && - ((ctrl[i][1]).Minus(ctrl[i][0])).Equals(along)) - { - continue; - } - return false; - } - - // yes, we are a surface of extrusion; copy the original curve and return - if(of) { - for(i = 0; i <= degm; i++) { - of->weight[i] = weight[i][0]; - of->ctrl[i] = ctrl[i][0]; - } - of->deg = degm; - *alongp = along; - } - return true; -} - -bool SSurface::IsCylinder(Vector *center, Vector *axis, double *r, - Vector *start, Vector *finish) -{ - SBezier sb; - if(!IsExtrusion(&sb, axis)) return false; - if(sb.deg != 2) return false; - - Vector t0 = (sb.ctrl[0]).Minus(sb.ctrl[1]), - t2 = (sb.ctrl[2]).Minus(sb.ctrl[1]), - r0 = axis->Cross(t0), - r2 = axis->Cross(t2); - - *center = Vector::AtIntersectionOfLines(sb.ctrl[0], (sb.ctrl[0]).Plus(r0), - sb.ctrl[2], (sb.ctrl[2]).Plus(r2), - NULL, NULL, NULL); - - double rd0 = center->Minus(sb.ctrl[0]).Magnitude(), - rd2 = center->Minus(sb.ctrl[2]).Magnitude(); - if(fabs(rd0 - rd2) > LENGTH_EPS) { - return false; - } - *r = rd0; - - Vector u = r0.WithMagnitude(1), - v = (axis->Cross(u)).WithMagnitude(1); - Point2d c2 = center->Project2d(u, v), - pa2 = (sb.ctrl[0]).Project2d(u, v).Minus(c2), - pb2 = (sb.ctrl[2]).Project2d(u, v).Minus(c2); - - double thetaa = atan2(pa2.y, pa2.x), // in fact always zero due to csys - thetab = atan2(pb2.y, pb2.x), - dtheta = WRAP_NOT_0(thetab - thetaa, 2*PI); - if(dtheta > PI) { - // Not possible with a second order Bezier arc; so we must have - // the points backwards. - dtheta = 2*PI - dtheta; - } - - if(fabs(sb.weight[1] - cos(dtheta/2)) > LENGTH_EPS) { - return false; - } - - *start = sb.ctrl[0]; - *finish = sb.ctrl[2]; - - return true; -} - -SSurface SSurface::FromPlane(Vector pt, Vector u, Vector v) { - SSurface ret; - ZERO(&ret); - - ret.degm = 1; - ret.degn = 1; - - ret.weight[0][0] = ret.weight[0][1] = 1; - ret.weight[1][0] = ret.weight[1][1] = 1; - - ret.ctrl[0][0] = pt; - ret.ctrl[0][1] = pt.Plus(u); - ret.ctrl[1][0] = pt.Plus(v); - ret.ctrl[1][1] = pt.Plus(v).Plus(u); - - return ret; -} - -SSurface SSurface::FromTransformationOf(SSurface *a, Vector t, Quaternion q, - bool includingTrims) -{ - SSurface ret; - ZERO(&ret); - - ret.h = a->h; - ret.color = a->color; - ret.face = a->face; - - 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; @@ -752,313 +328,3 @@ void SSurface::PointOnSurfaces(SSurface *s1, SSurface *s2, dbp("didn't converge (three surfaces intersecting)"); } -void SSurface::GetAxisAlignedBounding(Vector *ptMax, Vector *ptMin) { - *ptMax = Vector::From(VERY_NEGATIVE, VERY_NEGATIVE, VERY_NEGATIVE); - *ptMin = Vector::From(VERY_POSITIVE, VERY_POSITIVE, VERY_POSITIVE); - - int i, j; - for(i = 0; i <= degm; i++) { - for(j = 0; j <= degn; j++) { - (ctrl[i][j]).MakeMaxMin(ptMax, ptMin); - } - } -} - -bool SSurface::LineEntirelyOutsideBbox(Vector a, Vector b, bool segment) { - Vector amax, amin; - GetAxisAlignedBounding(&amax, &amin); - if(!Vector::BoundingBoxIntersectsLine(amax, amin, a, b, segment)) { - // The line segment could fail to intersect the bbox, but lie entirely - // within it and intersect the surface. - if(a.OutsideAndNotOn(amax, amin) && b.OutsideAndNotOn(amax, amin)) { - return true; - } - } - return false; -} - -void SSurface::MakeEdgesInto(SShell *shell, SEdgeList *sel, bool asUv, - SShell *useCurvesFrom) { - STrimBy *stb; - for(stb = trim.First(); stb; stb = trim.NextAfter(stb)) { - SCurve *sc = shell->curve.FindById(stb->curve); - - // We have the option to use the curves from another shell; this - // is relevant when generating the coincident edges while doing the - // Booleans, since the curves from the output shell will be split - // against any intersecting surfaces (and the originals aren't). - if(useCurvesFrom) { - sc = useCurvesFrom->curve.FindById(sc->newH); - } - - Vector prev, prevuv, ptuv; - bool inCurve = false, empty = true; - double u = 0, v = 0; - - int i, first, last, increment; - if(stb->backwards) { - first = sc->pts.n - 1; - last = 0; - increment = -1; - } else { - first = 0; - last = sc->pts.n - 1; - increment = 1; - } - for(i = first; i != (last + increment); i += increment) { - Vector *pt = &(sc->pts.elem[i]); - if(asUv) { - ClosestPointTo(*pt, &u, &v); - ptuv = Vector::From(u, v, 0); - if(inCurve) { - sel->AddEdge(prevuv, ptuv, sc->h.v, stb->backwards); - empty = false; - } - prevuv = ptuv; - } else { - if(inCurve) { - sel->AddEdge(prev, *pt, sc->h.v, stb->backwards); - empty = false; - } - prev = *pt; - } - - if(pt->Equals(stb->start)) inCurve = true; - if(pt->Equals(stb->finish)) inCurve = false; - } - if(inCurve) dbp("trim was unterminated"); - if(empty) dbp("trim was empty"); - } -} - -void SSurface::TriangulateInto(SShell *shell, SMesh *sm) { - SEdgeList el; - ZERO(&el); - - MakeEdgesInto(shell, &el, true); - - SPolygon poly; - ZERO(&poly); - if(el.AssemblePolygon(&poly, NULL, true)) { - int i, start = sm->l.n; - // Curved surfaces are triangulated in such a way as to minimize - // deviation between edges and surface; but doesn't matter for planes. - poly.UvTriangulateInto(sm, (degm == 1 && degn == 1) ? NULL : this); - - STriMeta meta = { face, color }; - for(i = start; i < sm->l.n; i++) { - STriangle *st = &(sm->l.elem[i]); - st->meta = meta; - st->an = NormalAt(st->a.x, st->a.y); - st->bn = NormalAt(st->b.x, st->b.y); - st->cn = NormalAt(st->c.x, st->c.y); - st->a = PointAt(st->a.x, st->a.y); - st->b = PointAt(st->b.x, st->b.y); - st->c = PointAt(st->c.x, st->c.y); - // Works out that my chosen contour direction is inconsistent with - // the triangle direction, sigh. - st->FlipNormal(); - } - } else { - dbp("failed to assemble polygon to trim nurbs surface in uv space"); - } - - el.Clear(); - poly.Clear(); -} - -//----------------------------------------------------------------------------- -// Reverse the parametrisation of one of our dimensions, which flips the -// normal. We therefore must reverse all our trim curves too. The uv -// coordinates change, but trim curves are stored as xyz so nothing happens -//----------------------------------------------------------------------------- -void SSurface::Reverse(void) { - int i, j; - for(i = 0; i < (degm+1)/2; i++) { - for(j = 0; j <= degn; j++) { - SWAP(Vector, ctrl[i][j], ctrl[degm-i][j]); - SWAP(double, weight[i][j], weight[degm-i][j]); - } - } - - STrimBy *stb; - for(stb = trim.First(); stb; stb = trim.NextAfter(stb)) { - stb->backwards = !stb->backwards; - SWAP(Vector, stb->start, stb->finish); - } -} - -void SSurface::Clear(void) { - trim.Clear(); -} - -void SShell::MakeFromExtrusionOf(SBezierLoopSet *sbls, Vector t0, Vector t1, - int color) -{ - ZERO(this); - - // Make the extrusion direction consistent with respect to the normal - // of the sketch we're extruding. - if((t0.Minus(t1)).Dot(sbls->normal) < 0) { - SWAP(Vector, t0, t1); - } - - // Define a coordinate system to contain the original sketch, and get - // a bounding box in that csys - Vector n = sbls->normal.ScaledBy(-1); - Vector u = n.Normal(0), v = n.Normal(1); - Vector orig = sbls->point; - double umax = 1e-10, umin = 1e10; - sbls->GetBoundingProjd(u, orig, &umin, &umax); - double vmax = 1e-10, vmin = 1e10; - sbls->GetBoundingProjd(v, orig, &vmin, &vmax); - // and now fix things up so that all u and v lie between 0 and 1 - orig = orig.Plus(u.ScaledBy(umin)); - orig = orig.Plus(v.ScaledBy(vmin)); - u = u.ScaledBy(umax - umin); - v = v.ScaledBy(vmax - vmin); - - // So we can now generate the top and bottom surfaces of the extrusion, - // planes within a translated (and maybe mirrored) version of that csys. - SSurface s0, s1; - s0 = SSurface::FromPlane(orig.Plus(t0), u, v); - s0.color = color; - s1 = SSurface::FromPlane(orig.Plus(t1).Plus(u), u.ScaledBy(-1), v); - s1.color = color; - 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 - // go through by loops so that we can assign the lines correctly. - SBezierLoop *sbl; - for(sbl = sbls->l.First(); sbl; sbl = sbls->l.NextAfter(sbl)) { - SBezier *sb; - - typedef struct { - hSCurve hc; - hSSurface hs; - } TrimLine; - List trimLines; - ZERO(&trimLines); - - for(sb = sbl->l.First(); sb; sb = sbl->l.NextAfter(sb)) { - // Generate the surface of extrusion of this curve, and add - // it to the list - SSurface ss = SSurface::FromExtrusionOf(sb, t0, t1); - ss.color = color; - hSSurface hsext = surface.AddAndAssignId(&ss); - - // Translate the curve by t0 and t1 to produce two trim curves - SCurve sc; - ZERO(&sc); - sc.isExact = true; - sc.exact = sb->TransformedBy(t0, Quaternion::IDENTITY); - (sc.exact).MakePwlInto(&(sc.pts)); - sc.surfA = hs0; - sc.surfB = hsext; - hSCurve hc0 = curve.AddAndAssignId(&sc); - - ZERO(&sc); - sc.isExact = true; - sc.exact = sb->TransformedBy(t1, Quaternion::IDENTITY); - (sc.exact).MakePwlInto(&(sc.pts)); - sc.surfA = hs1; - sc.surfB = hsext; - hSCurve hc1 = curve.AddAndAssignId(&sc); - - STrimBy stb0, stb1; - // The translated curves trim the flat top and bottom surfaces. - stb0 = STrimBy::EntireCurve(this, hc0, false); - stb1 = STrimBy::EntireCurve(this, hc1, true); - (surface.FindById(hs0))->trim.Add(&stb0); - (surface.FindById(hs1))->trim.Add(&stb1); - - // The translated curves also trim the surface of extrusion. - stb0 = STrimBy::EntireCurve(this, hc0, true); - stb1 = STrimBy::EntireCurve(this, hc1, false); - (surface.FindById(hsext))->trim.Add(&stb0); - (surface.FindById(hsext))->trim.Add(&stb1); - - // And form the trim line - Vector pt = sb->Finish(); - ZERO(&sc); - sc.isExact = true; - sc.exact = SBezier::From(pt.Plus(t0), pt.Plus(t1)); - (sc.exact).MakePwlInto(&(sc.pts)); - hSCurve hl = curve.AddAndAssignId(&sc); - // save this for later - TrimLine tl; - tl.hc = hl; - tl.hs = hsext; - trimLines.Add(&tl); - } - - int i; - for(i = 0; i < trimLines.n; i++) { - TrimLine *tl = &(trimLines.elem[i]); - SSurface *ss = surface.FindById(tl->hs); - - TrimLine *tlp = &(trimLines.elem[WRAP(i-1, trimLines.n)]); - - STrimBy stb; - stb = STrimBy::EntireCurve(this, tl->hc, true); - ss->trim.Add(&stb); - stb = STrimBy::EntireCurve(this, tlp->hc, false); - ss->trim.Add(&stb); - - (curve.FindById(tl->hc))->surfA = ss->h; - (curve.FindById(tlp->hc))->surfB = ss->h; - } - trimLines.Clear(); - } -} - -void SShell::MakeFromCopyOf(SShell *a) { - MakeFromTransformationOf(a, Vector::From(0, 0, 0), Quaternion::IDENTITY); -} - -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) { - SSurface *s; - for(s = surface.First(); s; s = surface.NextAfter(s)) { - s->TriangulateInto(this, sm); - } -} - -void SShell::Clear(void) { - SSurface *s; - for(s = surface.First(); s; s = surface.NextAfter(s)) { - s->Clear(); - } - surface.Clear(); - - SCurve *c; - for(c = curve.First(); c; c = curve.NextAfter(c)) { - c->Clear(); - } - curve.Clear(); -} - diff --git a/srf/surface.cpp b/srf/surface.cpp new file mode 100644 index 00000000..c43cd693 --- /dev/null +++ b/srf/surface.cpp @@ -0,0 +1,462 @@ +//----------------------------------------------------------------------------- +// Anything involving surfaces and sets of surfaces (i.e., shells); except +// for the real math, which is in ratpoly.cpp. +//----------------------------------------------------------------------------- +#include "../solvespace.h" + +SSurface SSurface::FromExtrusionOf(SBezier *sb, Vector t0, Vector t1) { + SSurface ret; + ZERO(&ret); + + ret.degm = sb->deg; + ret.degn = 1; + + int i; + for(i = 0; i <= ret.degm; i++) { + ret.ctrl[i][0] = (sb->ctrl[i]).Plus(t0); + ret.weight[i][0] = sb->weight[i]; + + ret.ctrl[i][1] = (sb->ctrl[i]).Plus(t1); + ret.weight[i][1] = sb->weight[i]; + } + + return ret; +} + +bool SSurface::IsExtrusion(SBezier *of, Vector *alongp) { + int i; + + if(degn != 1) return false; + + Vector along = (ctrl[0][1]).Minus(ctrl[0][0]); + for(i = 0; i <= degm; i++) { + if((fabs(weight[i][1] - weight[i][0]) < LENGTH_EPS) && + ((ctrl[i][1]).Minus(ctrl[i][0])).Equals(along)) + { + continue; + } + return false; + } + + // yes, we are a surface of extrusion; copy the original curve and return + if(of) { + for(i = 0; i <= degm; i++) { + of->weight[i] = weight[i][0]; + of->ctrl[i] = ctrl[i][0]; + } + of->deg = degm; + *alongp = along; + } + return true; +} + +bool SSurface::IsCylinder(Vector *center, Vector *axis, double *r, + Vector *start, Vector *finish) +{ + SBezier sb; + if(!IsExtrusion(&sb, axis)) return false; + if(sb.deg != 2) return false; + + Vector t0 = (sb.ctrl[0]).Minus(sb.ctrl[1]), + t2 = (sb.ctrl[2]).Minus(sb.ctrl[1]), + r0 = axis->Cross(t0), + r2 = axis->Cross(t2); + + *center = Vector::AtIntersectionOfLines(sb.ctrl[0], (sb.ctrl[0]).Plus(r0), + sb.ctrl[2], (sb.ctrl[2]).Plus(r2), + NULL, NULL, NULL); + + double rd0 = center->Minus(sb.ctrl[0]).Magnitude(), + rd2 = center->Minus(sb.ctrl[2]).Magnitude(); + if(fabs(rd0 - rd2) > LENGTH_EPS) { + return false; + } + *r = rd0; + + Vector u = r0.WithMagnitude(1), + v = (axis->Cross(u)).WithMagnitude(1); + Point2d c2 = center->Project2d(u, v), + pa2 = (sb.ctrl[0]).Project2d(u, v).Minus(c2), + pb2 = (sb.ctrl[2]).Project2d(u, v).Minus(c2); + + double thetaa = atan2(pa2.y, pa2.x), // in fact always zero due to csys + thetab = atan2(pb2.y, pb2.x), + dtheta = WRAP_NOT_0(thetab - thetaa, 2*PI); + if(dtheta > PI) { + // Not possible with a second order Bezier arc; so we must have + // the points backwards. + dtheta = 2*PI - dtheta; + } + + if(fabs(sb.weight[1] - cos(dtheta/2)) > LENGTH_EPS) { + return false; + } + + *start = sb.ctrl[0]; + *finish = sb.ctrl[2]; + + return true; +} + +SSurface SSurface::FromPlane(Vector pt, Vector u, Vector v) { + SSurface ret; + ZERO(&ret); + + ret.degm = 1; + ret.degn = 1; + + ret.weight[0][0] = ret.weight[0][1] = 1; + ret.weight[1][0] = ret.weight[1][1] = 1; + + ret.ctrl[0][0] = pt; + ret.ctrl[0][1] = pt.Plus(u); + ret.ctrl[1][0] = pt.Plus(v); + ret.ctrl[1][1] = pt.Plus(v).Plus(u); + + return ret; +} + +SSurface SSurface::FromTransformationOf(SSurface *a, Vector t, Quaternion q, + bool includingTrims) +{ + SSurface ret; + ZERO(&ret); + + ret.h = a->h; + ret.color = a->color; + ret.face = a->face; + + 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; +} + +void SSurface::GetAxisAlignedBounding(Vector *ptMax, Vector *ptMin) { + *ptMax = Vector::From(VERY_NEGATIVE, VERY_NEGATIVE, VERY_NEGATIVE); + *ptMin = Vector::From(VERY_POSITIVE, VERY_POSITIVE, VERY_POSITIVE); + + int i, j; + for(i = 0; i <= degm; i++) { + for(j = 0; j <= degn; j++) { + (ctrl[i][j]).MakeMaxMin(ptMax, ptMin); + } + } +} + +bool SSurface::LineEntirelyOutsideBbox(Vector a, Vector b, bool segment) { + Vector amax, amin; + GetAxisAlignedBounding(&amax, &amin); + if(!Vector::BoundingBoxIntersectsLine(amax, amin, a, b, segment)) { + // The line segment could fail to intersect the bbox, but lie entirely + // within it and intersect the surface. + if(a.OutsideAndNotOn(amax, amin) && b.OutsideAndNotOn(amax, amin)) { + return true; + } + } + return false; +} + +void SSurface::MakeEdgesInto(SShell *shell, SEdgeList *sel, bool asUv, + SShell *useCurvesFrom) +{ + STrimBy *stb; + for(stb = trim.First(); stb; stb = trim.NextAfter(stb)) { + SCurve *sc = shell->curve.FindById(stb->curve); + + // We have the option to use the curves from another shell; this + // is relevant when generating the coincident edges while doing the + // Booleans, since the curves from the output shell will be split + // against any intersecting surfaces (and the originals aren't). + if(useCurvesFrom) { + sc = useCurvesFrom->curve.FindById(sc->newH); + } + + Vector prev, prevuv, ptuv; + bool inCurve = false, empty = true; + double u = 0, v = 0; + + int i, first, last, increment; + if(stb->backwards) { + first = sc->pts.n - 1; + last = 0; + increment = -1; + } else { + first = 0; + last = sc->pts.n - 1; + increment = 1; + } + for(i = first; i != (last + increment); i += increment) { + Vector *pt = &(sc->pts.elem[i]); + if(asUv) { + ClosestPointTo(*pt, &u, &v); + ptuv = Vector::From(u, v, 0); + if(inCurve) { + sel->AddEdge(prevuv, ptuv, sc->h.v, stb->backwards); + empty = false; + } + prevuv = ptuv; + } else { + if(inCurve) { + sel->AddEdge(prev, *pt, sc->h.v, stb->backwards); + empty = false; + } + prev = *pt; + } + + if(pt->Equals(stb->start)) inCurve = true; + if(pt->Equals(stb->finish)) inCurve = false; + } + if(inCurve) dbp("trim was unterminated"); + if(empty) dbp("trim was empty"); + } +} + +void SSurface::TriangulateInto(SShell *shell, SMesh *sm) { + SEdgeList el; + ZERO(&el); + + MakeEdgesInto(shell, &el, true); + + SPolygon poly; + ZERO(&poly); + if(el.AssemblePolygon(&poly, NULL, true)) { + int i, start = sm->l.n; + // Curved surfaces are triangulated in such a way as to minimize + // deviation between edges and surface; but doesn't matter for planes. + poly.UvTriangulateInto(sm, (degm == 1 && degn == 1) ? NULL : this); + + STriMeta meta = { face, color }; + for(i = start; i < sm->l.n; i++) { + STriangle *st = &(sm->l.elem[i]); + st->meta = meta; + st->an = NormalAt(st->a.x, st->a.y); + st->bn = NormalAt(st->b.x, st->b.y); + st->cn = NormalAt(st->c.x, st->c.y); + st->a = PointAt(st->a.x, st->a.y); + st->b = PointAt(st->b.x, st->b.y); + st->c = PointAt(st->c.x, st->c.y); + // Works out that my chosen contour direction is inconsistent with + // the triangle direction, sigh. + st->FlipNormal(); + } + } else { + dbp("failed to assemble polygon to trim nurbs surface in uv space"); + } + + el.Clear(); + poly.Clear(); +} + +//----------------------------------------------------------------------------- +// Reverse the parametrisation of one of our dimensions, which flips the +// normal. We therefore must reverse all our trim curves too. The uv +// coordinates change, but trim curves are stored as xyz so nothing happens +//----------------------------------------------------------------------------- +void SSurface::Reverse(void) { + int i, j; + for(i = 0; i < (degm+1)/2; i++) { + for(j = 0; j <= degn; j++) { + SWAP(Vector, ctrl[i][j], ctrl[degm-i][j]); + SWAP(double, weight[i][j], weight[degm-i][j]); + } + } + + STrimBy *stb; + for(stb = trim.First(); stb; stb = trim.NextAfter(stb)) { + stb->backwards = !stb->backwards; + SWAP(Vector, stb->start, stb->finish); + } +} + +void SSurface::Clear(void) { + trim.Clear(); +} + +void SShell::MakeFromExtrusionOf(SBezierLoopSet *sbls, Vector t0, Vector t1, + int color) +{ + ZERO(this); + + // Make the extrusion direction consistent with respect to the normal + // of the sketch we're extruding. + if((t0.Minus(t1)).Dot(sbls->normal) < 0) { + SWAP(Vector, t0, t1); + } + + // Define a coordinate system to contain the original sketch, and get + // a bounding box in that csys + Vector n = sbls->normal.ScaledBy(-1); + Vector u = n.Normal(0), v = n.Normal(1); + Vector orig = sbls->point; + double umax = 1e-10, umin = 1e10; + sbls->GetBoundingProjd(u, orig, &umin, &umax); + double vmax = 1e-10, vmin = 1e10; + sbls->GetBoundingProjd(v, orig, &vmin, &vmax); + // and now fix things up so that all u and v lie between 0 and 1 + orig = orig.Plus(u.ScaledBy(umin)); + orig = orig.Plus(v.ScaledBy(vmin)); + u = u.ScaledBy(umax - umin); + v = v.ScaledBy(vmax - vmin); + + // So we can now generate the top and bottom surfaces of the extrusion, + // planes within a translated (and maybe mirrored) version of that csys. + SSurface s0, s1; + s0 = SSurface::FromPlane(orig.Plus(t0), u, v); + s0.color = color; + s1 = SSurface::FromPlane(orig.Plus(t1).Plus(u), u.ScaledBy(-1), v); + s1.color = color; + 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 + // go through by loops so that we can assign the lines correctly. + SBezierLoop *sbl; + for(sbl = sbls->l.First(); sbl; sbl = sbls->l.NextAfter(sbl)) { + SBezier *sb; + + typedef struct { + hSCurve hc; + hSSurface hs; + } TrimLine; + List trimLines; + ZERO(&trimLines); + + for(sb = sbl->l.First(); sb; sb = sbl->l.NextAfter(sb)) { + // Generate the surface of extrusion of this curve, and add + // it to the list + SSurface ss = SSurface::FromExtrusionOf(sb, t0, t1); + ss.color = color; + hSSurface hsext = surface.AddAndAssignId(&ss); + + // Translate the curve by t0 and t1 to produce two trim curves + SCurve sc; + ZERO(&sc); + sc.isExact = true; + sc.exact = sb->TransformedBy(t0, Quaternion::IDENTITY); + (sc.exact).MakePwlInto(&(sc.pts)); + sc.surfA = hs0; + sc.surfB = hsext; + hSCurve hc0 = curve.AddAndAssignId(&sc); + + ZERO(&sc); + sc.isExact = true; + sc.exact = sb->TransformedBy(t1, Quaternion::IDENTITY); + (sc.exact).MakePwlInto(&(sc.pts)); + sc.surfA = hs1; + sc.surfB = hsext; + hSCurve hc1 = curve.AddAndAssignId(&sc); + + STrimBy stb0, stb1; + // The translated curves trim the flat top and bottom surfaces. + stb0 = STrimBy::EntireCurve(this, hc0, false); + stb1 = STrimBy::EntireCurve(this, hc1, true); + (surface.FindById(hs0))->trim.Add(&stb0); + (surface.FindById(hs1))->trim.Add(&stb1); + + // The translated curves also trim the surface of extrusion. + stb0 = STrimBy::EntireCurve(this, hc0, true); + stb1 = STrimBy::EntireCurve(this, hc1, false); + (surface.FindById(hsext))->trim.Add(&stb0); + (surface.FindById(hsext))->trim.Add(&stb1); + + // And form the trim line + Vector pt = sb->Finish(); + ZERO(&sc); + sc.isExact = true; + sc.exact = SBezier::From(pt.Plus(t0), pt.Plus(t1)); + (sc.exact).MakePwlInto(&(sc.pts)); + hSCurve hl = curve.AddAndAssignId(&sc); + // save this for later + TrimLine tl; + tl.hc = hl; + tl.hs = hsext; + trimLines.Add(&tl); + } + + int i; + for(i = 0; i < trimLines.n; i++) { + TrimLine *tl = &(trimLines.elem[i]); + SSurface *ss = surface.FindById(tl->hs); + + TrimLine *tlp = &(trimLines.elem[WRAP(i-1, trimLines.n)]); + + STrimBy stb; + stb = STrimBy::EntireCurve(this, tl->hc, true); + ss->trim.Add(&stb); + stb = STrimBy::EntireCurve(this, tlp->hc, false); + ss->trim.Add(&stb); + + (curve.FindById(tl->hc))->surfA = ss->h; + (curve.FindById(tlp->hc))->surfB = ss->h; + } + trimLines.Clear(); + } +} + +void SShell::MakeFromCopyOf(SShell *a) { + MakeFromTransformationOf(a, Vector::From(0, 0, 0), Quaternion::IDENTITY); +} + +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) { + SSurface *s; + for(s = surface.First(); s; s = surface.NextAfter(s)) { + s->TriangulateInto(this, sm); + } +} + +void SShell::Clear(void) { + SSurface *s; + for(s = surface.First(); s; s = surface.NextAfter(s)) { + s->Clear(); + } + surface.Clear(); + + SCurve *c; + for(c = curve.First(); c; c = curve.NextAfter(c)) { + c->Clear(); + } + curve.Clear(); +} + diff --git a/textscreens.cpp b/textscreens.cpp index 18319820..f7dbc670 100644 --- a/textscreens.cpp +++ b/textscreens.cpp @@ -424,9 +424,6 @@ void TextWindow::ShowGroupInfo(void) { (asy || !asa ? "" : "assemble"), (asy && asa ? "assemble" : "")); } if(g->type == Group::IMPORTED) { - if(g->meshError.yes) { - Printf(false, "%Fx the parts interfere!"); - } bool sup = g->suppress; Printf(false, "%FtSUPPRESS%E %Fh%f%Ll%s%E%Fs%s%E / %Fh%f%Ll%s%E%Fs%s%E", &TextWindow::ScreenChangeSuppress, diff --git a/ui.h b/ui.h index 53aa5512..473f7c7c 100644 --- a/ui.h +++ b/ui.h @@ -246,6 +246,7 @@ public: MNU_COMMENT, // Analyze MNU_VOLUME, + MNU_INTERFERENCE, MNU_NAKED_EDGES, MNU_SHOW_DOF, MNU_TRACE_PT, diff --git a/undoredo.cpp b/undoredo.cpp index f8413597..0b3b5365 100644 --- a/undoredo.cpp +++ b/undoredo.cpp @@ -53,8 +53,7 @@ void SolveSpace::PushFromCurrentOnto(UndoStack *uk) { ZERO(&(dest.runningMesh)); ZERO(&(dest.thisShell)); ZERO(&(dest.runningShell)); - ZERO(&(dest.meshError)); - ZERO(&(dest.emphEdges)); + ZERO(&(dest.runningEdges)); ZERO(&(dest.remap)); src->remap.DeepCopyInto(&(dest.remap)); @@ -96,8 +95,7 @@ void SolveSpace::PopOntoCurrentFrom(UndoStack *uk) { g->runningMesh.Clear(); g->thisShell.Clear(); g->runningShell.Clear(); - g->meshError.interferesAt.Clear(); - g->emphEdges.Clear(); + g->runningEdges.Clear(); g->remap.Clear(); g->impMesh.Clear(); g->impEntity.Clear();