From 248f74547e7c08691a2ccc59e12d17c2cbbc0b98 Mon Sep 17 00:00:00 2001 From: Jonathan Westhues Date: Sun, 25 May 2008 05:11:44 -0800 Subject: [PATCH] Add user interface to specify union/difference for extrudes, and implement that. Also make solver work only between the first and last visible group; earlier can just work from previous solve result, and later don't matter. There's some issues with the csg code; it will eventually produce an open mesh, which is very bad. Not sure whether that's a logic bug, or a numerical issue; still generating absurd triangles pretty routinely. [git-p4: depot-paths = "//depot/solvespace/": change = 1741] --- constraint.cpp | 2 +- drawconstraint.cpp | 2 +- file.cpp | 1 + graphicswin.cpp | 48 ++++++++--------------------- mesh.cpp | 28 ++++++++++++++++- polygon.h | 2 ++ sketch.cpp | 77 +++++++++++++++++++++++++++++++++------------- sketch.h | 7 +++-- solvespace.cpp | 50 +++++++++++++++++++++--------- solvespace.h | 1 + textwin.cpp | 31 +++++++++++++++---- ui.h | 1 + 12 files changed, 166 insertions(+), 84 deletions(-) diff --git a/constraint.cpp b/constraint.cpp index 48ae3c4..f073d02 100644 --- a/constraint.cpp +++ b/constraint.cpp @@ -276,7 +276,7 @@ void Constraint::MenuConstrain(int id) { break; case GraphicsWindow::MNU_SOLVE_NOW: - SS.GenerateAll(true); + SS.GenerateAll(true, 0, 10000); return; case GraphicsWindow::MNU_SOLVE_AUTO: diff --git a/drawconstraint.cpp b/drawconstraint.cpp index 25646f9..9881e47 100644 --- a/drawconstraint.cpp +++ b/drawconstraint.cpp @@ -83,7 +83,7 @@ void Constraint::DrawOrGetDistance(Vector *labelPos) { Vector gu = SS.GW.projUp.ScaledBy(1/SS.GW.scale); Vector gn = (gr.Cross(gu)).WithMagnitude(1/SS.GW.scale); - glxColor3d(1, 0.2, 1); + glxColor3d(1, 0.4, 1); switch(type) { case PT_PT_DISTANCE: { Vector ap = SS.GetEntity(ptA)->PointGetNum(); diff --git a/file.cpp b/file.cpp index e4b5c4d..866ae7f 100644 --- a/file.cpp +++ b/file.cpp @@ -51,6 +51,7 @@ const SolveSpace::SaveTable SolveSpace::SAVED[] = { { 'g', "Group.opA.v", 'x', &(SS.sv.g.opA.v) }, { 'g', "Group.opB.v", 'x', &(SS.sv.g.opB.v) }, { 'g', "Group.subtype", 'd', &(SS.sv.g.subtype) }, + { 'g', "Group.meshCombine", 'd', &(SS.sv.g.meshCombine) }, { 'g', "Group.wrkpl.q.w", 'f', &(SS.sv.g.wrkpl.q.w) }, { 'g', "Group.wrkpl.q.vx", 'f', &(SS.sv.g.wrkpl.q.vx) }, { 'g', "Group.wrkpl.q.vy", 'f', &(SS.sv.g.wrkpl.q.vy) }, diff --git a/graphicswin.cpp b/graphicswin.cpp index 9f70f1b..cb3ec77 100644 --- a/graphicswin.cpp +++ b/graphicswin.cpp @@ -102,12 +102,19 @@ void GraphicsWindow::Init(void) { projRight.x = 1; projRight.y = projRight.z = 0; projUp.y = 1; projUp.z = projUp.x = 0; - EnsureValidActives(); - // Start locked on to the XY plane. hRequest r = Request::HREQUEST_REFERENCE_XY; activeWorkplane = r.entity(0); + // And with the latest visible group active + int i; + for(i = 0; i < SS.group.n; i++) { + Group *g = &(SS.group.elem[i]); + if(g->visible) activeGroup = g->h; + } + + EnsureValidActives(); + showWorkplanes = true; showNormals = true; showPoints = true; @@ -1144,11 +1151,10 @@ void GraphicsWindow::Paint(int w, int h) { glxUnlockColor(); int i, a; - // Draw the groups; this fills the polygons, if requested. + // Draw the groups; this fills the polygons in a drawing group, and + // draws the solid mesh. if(showSolids) { - for(i = 0; i < SS.group.n; i++) { - SS.group.elem[i].Draw(); - } + (SS.GetGroup(activeGroup))->Draw(); } // First, draw the entire scene. We don't necessarily want to draw @@ -1180,35 +1186,5 @@ void GraphicsWindow::Paint(int w, int h) { for(i = 0; i < MAX_SELECTED; i++) { selection[i].Draw(); } - - if(SS.group.n >= 5) { - SMesh *ma = &(SS.group.elem[2].mesh); - SMesh *mb = &(SS.group.elem[4].mesh); - - SBsp3 *pa = SBsp3::FromMesh(ma); - SBsp3 *pb = SBsp3::FromMesh(mb); - - SMesh br; ZERO(&br); - br.flipNormal = true; - br.keepCoplanar = false; - br.AddAgainstBsp(mb, pa); - - br.flipNormal = false; - br.keepCoplanar = false; - br.AddAgainstBsp(ma, pb); - - dbp("triangles in = %d %d out = %d", ma->l.n, mb->l.n, br.l.n); - - glEnable(GL_DEPTH_TEST); - glEnable(GL_LIGHTING); - glxFillMesh(&br); - glDisable(GL_LIGHTING); - glxLockColorTo(0, 1, 0); - glEnable(GL_DEPTH_TEST); - glxDebugMesh(&br); - - br.Clear(); - FreeAllTemporary(); - } } diff --git a/mesh.cpp b/mesh.cpp index d92e671..ea25549 100644 --- a/mesh.cpp +++ b/mesh.cpp @@ -51,6 +51,7 @@ void SMesh::GetBounding(Vector *vmax, Vector *vmin) { void SMesh::AddAgainstBsp(SMesh *srcm, SBsp3 *bsp3) { int i; + for(i = 0; i < srcm->l.n; i++) { STriangle *st = &(srcm->l.elem[i]); int pn = l.n; @@ -68,6 +69,32 @@ void SMesh::AddAgainstBsp(SMesh *srcm, SBsp3 *bsp3) { } } +void SMesh::MakeFromUnion(SMesh *a, SMesh *b) { + SBsp3 *bspa = SBsp3::FromMesh(a); + SBsp3 *bspb = SBsp3::FromMesh(b); + + flipNormal = false; + keepCoplanar = false; + AddAgainstBsp(b, bspa); + + flipNormal = false; + keepCoplanar = true; + AddAgainstBsp(a, bspb); +} + +void SMesh::MakeFromDifference(SMesh *a, SMesh *b) { + SBsp3 *bspa = SBsp3::FromMesh(a); + SBsp3 *bspb = SBsp3::FromMesh(b); + + flipNormal = true; + keepCoplanar = false; + AddAgainstBsp(b, bspa); + + flipNormal = false; + keepCoplanar = false; + AddAgainstBsp(a, bspb); +} + SBsp2 *SBsp2::Alloc(void) { return (SBsp2 *)AllocTemporary(sizeof(SBsp2)); } SBsp3 *SBsp3::Alloc(void) { return (SBsp3 *)AllocTemporary(sizeof(SBsp3)); } @@ -153,7 +180,6 @@ alt: } else { // I suppose this actually is allowed to happen, if the coplanar // face is the leaf, and all of its neighbors are earlier in tree? - dbp("insert in plane"); InsertInPlane(false, tr, instead); } } else { diff --git a/polygon.h b/polygon.h index 280bee3..f6f0c27 100644 --- a/polygon.h +++ b/polygon.h @@ -178,6 +178,8 @@ public: void GetBounding(Vector *vmax, Vector *vmin); void AddAgainstBsp(SMesh *srcm, SBsp3 *bsp3); + void MakeFromUnion(SMesh *a, SMesh *b); + void MakeFromDifference(SMesh *a, SMesh *b); }; #endif diff --git a/sketch.cpp b/sketch.cpp index edb8832..30770d6 100644 --- a/sketch.cpp +++ b/sketch.cpp @@ -381,15 +381,27 @@ void Group::CopyEntity(hEntity in, int a, hParam dx, hParam dy, hParam dz, SS.entity.Add(&en); } -void Group::MakePolygons(void) { +SMesh *Group::PreviousGroupMesh(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].mesh); +} + +void Group::MakePolygons(void) { poly.Clear(); - mesh.Clear(); SEdgeList edges; ZERO(&edges); + SMesh outm; + ZERO(&outm); - if(type == DRAWING_3D || type == DRAWING_WORKPLANE) { + if(type == DRAWING_3D || type == DRAWING_WORKPLANE || + type == ROTATE || type == TRANSLATE) + { int i; for(i = 0; i < SS.entity.n; i++) { Entity *e = &(SS.entity.elem[i]); @@ -427,7 +439,6 @@ void Group::MakePolygons(void) { SMesh srcm; ZERO(&srcm); (src->poly).TriangulateInto(&srcm); - SMesh outm; ZERO(&outm); // Do the bottom; that has normal pointing opposite from translate for(i = 0; i < srcm.l.n; i++) { STriangle *st = &(srcm.l.elem[i]); @@ -435,9 +446,9 @@ void Group::MakePolygons(void) { bt = (st->b).Plus(tbot), ct = (st->c).Plus(tbot); if(flipBottom) { - mesh.AddTriangle(ct, bt, at); + outm.AddTriangle(ct, bt, at); } else { - mesh.AddTriangle(at, bt, ct); + outm.AddTriangle(at, bt, ct); } } // And the top; that has the normal pointing the same dir as translate @@ -447,9 +458,9 @@ void Group::MakePolygons(void) { bt = (st->b).Plus(ttop), ct = (st->c).Plus(ttop); if(flipBottom) { - mesh.AddTriangle(at, bt, ct); + outm.AddTriangle(at, bt, ct); } else { - mesh.AddTriangle(ct, bt, at); + outm.AddTriangle(ct, bt, at); } } srcm.Clear(); @@ -463,26 +474,50 @@ void Group::MakePolygons(void) { Vector abot = (edge->a).Plus(tbot), bbot = (edge->b).Plus(tbot); Vector atop = (edge->a).Plus(ttop), btop = (edge->b).Plus(ttop); if(flipBottom) { - mesh.AddTriangle(bbot, abot, atop); - mesh.AddTriangle(bbot, atop, btop); + outm.AddTriangle(bbot, abot, atop); + outm.AddTriangle(bbot, atop, btop); } else { - mesh.AddTriangle(abot, bbot, atop); - mesh.AddTriangle(bbot, btop, atop); + outm.AddTriangle(abot, bbot, atop); + outm.AddTriangle(bbot, btop, atop); } } } edges.Clear(); + + // So our group's mesh appears in outm. Combine this with the previous + // group's mesh, using the requested operation. + mesh.Clear(); + SMesh *a = PreviousGroupMesh(); + if(meshCombine == COMBINE_AS_UNION) { + mesh.MakeFromUnion(a, &outm); + } else { + mesh.MakeFromDifference(a, &outm); + } + outm.Clear(); } void Group::Draw(void) { - if(!visible) return; + // Show this even if the group is not visible. It's already possible + // to show or hide just this with the "show solids" flag. - - GLfloat mpf[] = { 0.4f, 0.4f, 0.4f, 1.0 }; - glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, mpf); + if(type == DRAWING_3D || type == DRAWING_WORKPLANE) { + GLfloat mpf[] = { 0.1f, 0.1f, 0.1f, 1.0 }; + glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, mpf); + } else { + GLfloat mpf[] = { 0.3f, 0.3f, 0.3f, 1.0 }; + glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, mpf); + } + // The back faces are drawn in red; should never seem them, since we + // draw closed shells, so that's a debugging aid. GLfloat mpb[] = { 1.0f, 0.1f, 0.1f, 1.0 }; glMaterialfv(GL_BACK, GL_AMBIENT_AND_DIFFUSE, mpb); + glEnable(GL_LIGHTING); + glxFillMesh(&mesh); + glDisable(GL_LIGHTING); + +// glxDebugMesh(&mesh); + if(polyError.yes) { glxColor4d(1, 0, 0, 0.2); glLineWidth(10); @@ -498,13 +533,11 @@ void Group::Draw(void) { glxWriteText("not closed contour!"); glPopMatrix(); } else { -// glxFillPolygon(&poly); + glxColor4d(0, 1.0, 1.0, 0.1); + glPolygonOffset(-1, -1); + glxFillPolygon(&poly); + glPolygonOffset(0, 0); } - glEnable(GL_LIGHTING); -// glxFillMesh(&mesh); - glDisable(GL_LIGHTING); - -// glxDebugMesh(&mesh); } hParam Request::AddParam(IdList *param, hParam hp) { diff --git a/sketch.h b/sketch.h index a032b9a..f1d894c 100644 --- a/sketch.h +++ b/sketch.h @@ -84,8 +84,6 @@ public: static const int TRANSLATE = 5030; int type; - bool solved; - hGroup opA; hGroup opB; bool visible; @@ -113,6 +111,10 @@ public: } polyError; SMesh mesh; + static const int COMBINE_AS_UNION = 0; + static const int COMBINE_AS_DIFFERENCE = 1; + int meshCombine; + NameStr name; char *DescriptionString(void); @@ -131,6 +133,7 @@ public: void GenerateEquations(IdList *l); + SMesh *PreviousGroupMesh(void); void MakePolygons(void); void Draw(void); diff --git a/solvespace.cpp b/solvespace.cpp index fd5fcec..e362a3e 100644 --- a/solvespace.cpp +++ b/solvespace.cpp @@ -9,11 +9,11 @@ void SolveSpace::Init(char *cmdLine) { LoadFromFile(cmdLine); } + GenerateAll(false, 0, INT_MAX); + TW.Init(); GW.Init(); - GenerateAll(false); - TW.Show(); } @@ -122,6 +122,21 @@ bool SolveSpace::PruneConstraints(hGroup hg) { } void SolveSpace::GenerateAll(bool andSolve) { + int i; + int firstShown = INT_MAX, lastShown = 0; + // The references don't count, so start from group 1 + for(i = 1; i < group.n; i++) { + if(group.elem[i].visible) { + firstShown = min(firstShown, i); + lastShown = max(lastShown, i); + } + } + // Even if nothing is shown, we have to keep going; the entities get + // generated for hidden groups, even though they're not solved. + GenerateAll(andSolve, firstShown, lastShown); +} + +void SolveSpace::GenerateAll(bool andSolve, int first, int last) { int i, j; while(PruneOrphans()) @@ -132,12 +147,6 @@ void SolveSpace::GenerateAll(bool andSolve) { param.MoveSelfInto(&prev); entity.Clear(); - for(i = 0; i < group.n; i++) { - group.elem[i].solved = false; - } - - // For now, solve the groups in given order; should discover the - // correct order later. for(i = 0; i < group.n; i++) { Group *g = &(group.elem[i]); @@ -172,13 +181,24 @@ void SolveSpace::GenerateAll(bool andSolve) { if(g->h.v == Group::HGROUP_REFERENCES.v) { ForceReferences(); - group.elem[0].solved = true; } else { - // Solve this group. - if(andSolve) SolveGroup(g->h); - } + if(i >= first && i <= last) { + // The group falls inside the range, so really solve it, + // and then regenerate the mesh based on the solved stuff. + if(andSolve) SolveGroup(g->h); + g->MakePolygons(); + } else { + // The group falls outside the range, so just assume that + // it's good wherever we left it. The mesh is unchanged, + // and the parameters must be marked as known. + for(j = 0; j < param.n; j++) { + Param *newp = &(param.elem[j]); - g->MakePolygons(); + Param *prevp = prev.FindByIdNoOops(newp->h); + if(prevp) newp->known = true; + } + } + } } prev.Clear(); @@ -217,7 +237,7 @@ pruned: param.Clear(); prev.MoveSelfInto(¶m); // Try again - GenerateAll(andSolve); + GenerateAll(andSolve, first, last); } void SolveSpace::ForceReferences(void) { @@ -299,8 +319,8 @@ void SolveSpace::MenuFile(int id) { case GraphicsWindow::MNU_NEW: SS.NewFile(); SS.GenerateAll(false); - SS.GW.Init(); SS.TW.Init(); + SS.GW.Init(); SS.TW.Show(); break; diff --git a/solvespace.h b/solvespace.h index faefb90..b85e13c 100644 --- a/solvespace.h +++ b/solvespace.h @@ -229,6 +229,7 @@ public: bool PruneConstraints(hGroup hg); void GenerateAll(bool andSolve); + void GenerateAll(bool andSolve, int first, int last); bool SolveGroup(hGroup hg); void ForceReferences(void); diff --git a/textwin.cpp b/textwin.cpp index 66f254e..11e6181 100644 --- a/textwin.cpp +++ b/textwin.cpp @@ -266,6 +266,9 @@ void TextWindow::ScreenToggleGroupShown(int link, DWORD v) { hGroup hg = { v }; Group *g = SS.GetGroup(hg); g->visible = !(g->visible); + // If a group was just shown, then it might not have been generated + // previously, so regenerate. + SS.GW.GeneratePerSolving(); } void TextWindow::ScreenShowGroupsSpecial(int link, DWORD v) { int i; @@ -341,9 +344,19 @@ void TextWindow::ScreenChangeExtrudeSides(int link, DWORD v) { Group *g = SS.GetGroup(SS.TW.shown->group); if(g->subtype == Group::EXTRUDE_ONE_SIDED) { g->subtype = Group::EXTRUDE_TWO_SIDED; - } else { + } else if(g->subtype == Group::EXTRUDE_TWO_SIDED) { g->subtype = Group::EXTRUDE_ONE_SIDED; - } + } else oops(); + SS.GW.GeneratePerSolving(); + SS.GW.ClearSuper(); +} +void TextWindow::ScreenChangeMeshCombine(int link, DWORD v) { + Group *g = SS.GetGroup(SS.TW.shown->group); + if(g->meshCombine == Group::COMBINE_AS_DIFFERENCE) { + g->meshCombine = Group::COMBINE_AS_UNION; + } else if(g->meshCombine == Group::COMBINE_AS_UNION) { + g->meshCombine = Group::COMBINE_AS_DIFFERENCE; + } else oops(); SS.GW.GeneratePerSolving(); SS.GW.ClearSuper(); } @@ -361,12 +374,18 @@ void TextWindow::ShowGroupInfo(void) { if(g->type == Group::EXTRUDE) { bool one = (g->subtype == Group::EXTRUDE_ONE_SIDED); - Printf(true, "%FtEXTRUDE%E one-sided: %Fh%f%Ll%s%E%Fs%s%E", + Printf(true, "%FtEXTRUDE%E %Fh%f%Ll%s%E%Fs%s%E / %Fh%f%Ll%s%E%Fs%s%E", &TextWindow::ScreenChangeExtrudeSides, - (one ? "" : "no"), (one ? "yes" : "")); - Printf(false, " two-sided: %Fh%f%Ll%s%E%Fs%s%E", + (one ? "" : "one side"), (one ? "one-side" : ""), &TextWindow::ScreenChangeExtrudeSides, - (!one ? "" : "no"), (!one ? "yes" : "")); + (!one ? "" : "two sides"), (!one ? "two sides" : "")); + + bool diff = (g->meshCombine == Group::COMBINE_AS_DIFFERENCE); + Printf(false, "%FtCOMBINE%E %Fh%f%Ll%s%E%Fs%s%E / %Fh%f%Ll%s%E%Fs%s%E", + &TextWindow::ScreenChangeMeshCombine, + (!diff ? "" : "as union"), (!diff ? "as union" : ""), + &TextWindow::ScreenChangeMeshCombine, + (diff ? "" : "as difference"), (diff ? "as difference" : "")); } Printf(true, "%Ftrequests in group"); diff --git a/ui.h b/ui.h index 79fd719..d1aeae6 100644 --- a/ui.h +++ b/ui.h @@ -74,6 +74,7 @@ public: static void ScreenSelectRequest(int link, DWORD v); static void ScreenSelectConstraint(int link, DWORD v); static void ScreenChangeExtrudeSides(int link, DWORD v); + static void ScreenChangeMeshCombine(int link, DWORD v); static void ScreenNavigation(int link, DWORD v); };