From 24fc65a71cdc72510ddaf0816caee09832264b0c Mon Sep 17 00:00:00 2001 From: EvilSpirit Date: Mon, 14 Mar 2016 22:14:24 +0600 Subject: [PATCH] Allow rendering solid outlines using a distinct style. A new button is added, "Show/hide outline of solid model". When the outline is hidden, it is rendered using the "solid edge" style. When the outline is shown, it is rendered using the "outline" style. In SolveSpace's true WYSIWYG tradition, the 2d view export follows the rendered view exactly. Moreover, shell edges are not rendered anymore, since there is not much need in them anymore and not drawing them lessens the overlap between various kinds of lines, which already includes entities, solid edges and outlines. --- src/export.cpp | 3 +- src/glhelper.cpp | 20 ++++++++++-- src/graphicswin.cpp | 1 + src/group.cpp | 1 + src/groupmesh.cpp | 21 ++++++++++--- src/icons/outlines.png | Bin 0 -> 930 bytes src/mesh.cpp | 68 +++++++++++++++++++++++++++++++++++------ src/polygon.h | 24 +++++++++++++-- src/sketch.h | 2 ++ src/solvespace.h | 2 ++ src/style.cpp | 1 + src/textwin.cpp | 1 + src/ui.h | 1 + src/undoredo.cpp | 1 + 14 files changed, 128 insertions(+), 18 deletions(-) create mode 100644 src/icons/outlines.png diff --git a/src/export.cpp b/src/export.cpp index 1e39627..b6baa9a 100644 --- a/src/export.cpp +++ b/src/export.cpp @@ -328,7 +328,8 @@ void SolveSpaceUI::ExportLinesAndMesh(SEdgeList *sel, SBezierList *sbl, SMesh *s // to back-facing. if(SS.GW.showEdges) { root->MakeCertainEdgesInto(sel, SKdNode::TURNING_EDGES, - false, NULL, NULL); + /*coplanarIsInter=*/false, NULL, NULL, + GW.showOutlines ? Style::OUTLINE : Style::SOLID_EDGE); } root->ClearTags(); diff --git a/src/glhelper.cpp b/src/glhelper.cpp index 8f706ee..e4f6bb0 100644 --- a/src/glhelper.cpp +++ b/src/glhelper.cpp @@ -201,7 +201,7 @@ static void FatLineEndcap(Vector p, Vector u, Vector v) } void ssglLine(const Vector &a, const Vector &b, double pixelWidth, bool maybeFat) { - if(!maybeFat || pixelWidth < 3.0) { + if(!maybeFat || pixelWidth <= 3.0) { glBegin(GL_LINES); ssglVertex3v(a); ssglVertex3v(b); @@ -213,7 +213,7 @@ void ssglLine(const Vector &a, const Vector &b, double pixelWidth, bool maybeFat void ssglPoint(Vector p, double pixelSize) { - if(/*!maybeFat || */pixelSize < 3.0) { + if(/*!maybeFat || */pixelSize <= 3.0) { glBegin(GL_LINES); Vector u = SS.GW.projRight.WithMagnitude(pixelSize / SS.GW.scale / 2.0); ssglVertex3v(p.Minus(u)); @@ -587,6 +587,22 @@ void ssglDrawEdges(SEdgeList *el, bool endpointsToo, hStyle hs) } } +void ssglDrawOutlines(SOutlineList *sol, Vector projDir, hStyle hs) +{ + double lineWidth = Style::Width(hs); + int stippleType = Style::PatternType(hs); + double stippleScale = Style::StippleScaleMm(hs); + ssglLineWidth((float)lineWidth); + ssglColorRGB(Style::Color(hs)); + + sol->FillOutlineTags(projDir); + for(SOutline *so = sol->l.First(); so; so = sol->l.NextAfter(so)) { + if(!so->tag) continue; + ssglStippledLine(so->a, so->b, lineWidth, stippleType, stippleScale, + /*maybeFat=*/true); + } +} + void ssglDebugMesh(SMesh *m) { int i; diff --git a/src/graphicswin.cpp b/src/graphicswin.cpp index 894c48f..dc8d70f 100644 --- a/src/graphicswin.cpp +++ b/src/graphicswin.cpp @@ -233,6 +233,7 @@ void GraphicsWindow::Init(void) { showShaded = true; showEdges = true; showMesh = false; + showOutlines = false; showTextWindow = true; ShowTextWindow(showTextWindow); diff --git a/src/group.cpp b/src/group.cpp index db0cf4b..1e612a6 100644 --- a/src/group.cpp +++ b/src/group.cpp @@ -29,6 +29,7 @@ void Group::Clear(void) { runningShell.Clear(); displayMesh.Clear(); displayEdges.Clear(); + displayOutlines.Clear(); impMesh.Clear(); impShell.Clear(); impEntity.Clear(); diff --git a/src/groupmesh.cpp b/src/groupmesh.cpp index 8889b24..80f9f57 100644 --- a/src/groupmesh.cpp +++ b/src/groupmesh.cpp @@ -378,12 +378,14 @@ void Group::GenerateDisplayItems(void) { displayMesh.MakeFromCopyOf(&(pg->displayMesh)); displayEdges.Clear(); + displayOutlines.Clear(); if(SS.GW.showEdges) { SEdge *se; SEdgeList *src = &(pg->displayEdges); for(se = src->l.First(); se; se = src->l.NextAfter(se)) { displayEdges.l.Add(se); } + displayOutlines.MakeFromCopyOf(&pg->displayOutlines); } } else { // We do contribute new solid model, so we have to triangulate the @@ -401,17 +403,20 @@ void Group::GenerateDisplayItems(void) { } displayEdges.Clear(); + displayOutlines.Clear(); if(SS.GW.showEdges) { if(runningMesh.l.n > 0) { // Triangle mesh only; no shell or emphasized edges. - runningMesh.MakeCertainEdgesInto(&displayEdges, SKdNode::EMPHASIZED_EDGES); + runningMesh.MakeCertainEdgesAndOutlinesInto( + &displayEdges, &displayOutlines, SKdNode::EMPHASIZED_EDGES); } else { if(SS.exportMode) { - displayMesh.MakeCertainEdgesInto(&displayEdges, SKdNode::SHARP_EDGES); + displayMesh.MakeCertainEdgesAndOutlinesInto( + &displayEdges, &displayOutlines, SKdNode::SHARP_EDGES); } else { - runningShell.MakeEdgesInto(&displayEdges); - displayMesh.MakeCertainEdgesInto(&displayEdges, SKdNode::EMPHASIZED_EDGES); + displayMesh.MakeCertainEdgesAndOutlinesInto( + &displayEdges, &displayOutlines, SKdNode::EMPHASIZED_EDGES); } } } @@ -495,15 +500,23 @@ void Group::DrawDisplayItems(int t) { } if(SS.GW.showEdges) { + Vector projDir = SS.GW.projRight.Cross(SS.GW.projUp); + glDepthMask(GL_FALSE); if(SS.GW.showHdnLines) { ssglDepthRangeOffset(0); glDepthFunc(GL_GREATER); ssglDrawEdges(&displayEdges, false, { Style::HIDDEN_EDGE }); + ssglDrawOutlines(&displayOutlines, projDir, { Style::HIDDEN_EDGE }); glDepthFunc(GL_LEQUAL); } ssglDepthRangeOffset(2); ssglDrawEdges(&displayEdges, false, { Style::SOLID_EDGE }); + if(SS.GW.showOutlines) { + ssglDrawOutlines(&displayOutlines, projDir, { Style::OUTLINE }); + } else { + ssglDrawOutlines(&displayOutlines, projDir, { Style::SOLID_EDGE }); + } glDepthMask(GL_TRUE); } diff --git a/src/icons/outlines.png b/src/icons/outlines.png new file mode 100644 index 0000000000000000000000000000000000000000..b1c95ab30192e9ce96853fd68f03129e89a1b070 GIT binary patch literal 930 zcmV;T16}-yP)(_`g8%^e{{R4h=l}px2mk>USO5SzmjD14Z`WEM zkN^M!C`m*?RCwB?mOn^(Z4}2pHxxwzlca9JMY@?4f(;>oL%n&p6_|ipXZMN zD13w6Zf9+64V%q|q9_Q0@O=n(cXy1Aj#4NT_zgAR6+WMja5#+3W&1GSS&0qF4EiEs~QW10=v7r+}zwSHa13gclYN+osOoars^EH-EM-xAhor%s^Rwb zmhJ6rmX?+@Y1!P|WMN?e0E59$*}?q$Jmcfz8f7#ZB^HY@H#et7;_-L@7#<#0$LaO@ z$__?GM${ic5YXv#I2;a!hK2w%Y56~>F&d2kWV2ZY1_p4sTxx;Kkv}~>sq)v9R_vl^ zG|K(`y=wT|m6tepd3mX7!t3j+DpynFnsO6=!`F>fO_6K^{aguGS67^$pVQUVg~4FJ>-7={1ZZezDBneXzaPKfuNuPe*4hNzrmaL{4WV2azc6K;8IM5uNLb+#)$z;N2vmwhe z&CSh-qDXstJCY=6E}3GnNG_LSYio;0Bto%REOGqL0M@)j9Fs*{iU0rr07*qoM6N<$ Eg1f7$-~a#s literal 0 HcmV?d00001 diff --git a/src/mesh.cpp b/src/mesh.cpp index b7f14d1..b3f4de8 100644 --- a/src/mesh.cpp +++ b/src/mesh.cpp @@ -91,9 +91,10 @@ void SMesh::MakeEdgesInPlaneInto(SEdgeList *sel, Vector n, double d) { m.Clear(); } -void SMesh::MakeCertainEdgesInto(SEdgeList *sel, int type) { +void SMesh::MakeCertainEdgesAndOutlinesInto(SEdgeList *sel, SOutlineList *sol, int type) { SKdNode *root = SKdNode::From(this); root->MakeCertainEdgesInto(sel, type, false, NULL, NULL); + root->MakeOutlinesInto(sol); } //----------------------------------------------------------------------------- @@ -923,8 +924,8 @@ static bool CheckAndAddTrianglePair(std::set // * emphasized edges (i.e., edges where a triangle from one face joins // a triangle from a different face) //----------------------------------------------------------------------------- -void SKdNode::MakeCertainEdgesInto(SEdgeList *sel, int how, - bool coplanarIsInter, bool *inter, bool *leaky) +void SKdNode::MakeCertainEdgesInto(SEdgeList *sel, int how, bool coplanarIsInter, + bool *inter, bool *leaky, int auxA) { if(inter) *inter = false; if(leaky) *leaky = false; @@ -946,18 +947,18 @@ void SKdNode::MakeCertainEdgesInto(SEdgeList *sel, int how, switch(how) { case NAKED_OR_SELF_INTER_EDGES: if(info.count != 1) { - sel->AddEdge(a, b); + sel->AddEdge(a, b, auxA); if(leaky) *leaky = true; } if(info.intersectsMesh) { - sel->AddEdge(a, b); + sel->AddEdge(a, b, auxA); if(inter) *inter = true; } break; case SELF_INTER_EDGES: if(info.intersectsMesh) { - sel->AddEdge(a, b); + sel->AddEdge(a, b, auxA); if(inter) *inter = true; } break; @@ -972,7 +973,7 @@ void SKdNode::MakeCertainEdgesInto(SEdgeList *sel, int how, // This triangle is back-facing (or on edge), and // this edge has exactly one mate, and that mate is // front-facing. So this is a turning edge. - sel->AddEdge(a, b, Style::SOLID_EDGE); + sel->AddEdge(a, b, auxA); } break; @@ -984,7 +985,7 @@ void SKdNode::MakeCertainEdgesInto(SEdgeList *sel, int how, // different faces; either really different faces, // or one is from a face and the other is zero (i.e., // not from a face). - sel->AddEdge(a, b); + sel->AddEdge(a, b, auxA); } break; @@ -1008,7 +1009,7 @@ void SKdNode::MakeCertainEdgesInto(SEdgeList *sel, int how, break; // The two triangles that join at this edge meet at a sharp // angle. This implies they come from different faces. - sel->AddEdge(a, b); + sel->AddEdge(a, b, auxA); } } break; @@ -1021,3 +1022,52 @@ void SKdNode::MakeCertainEdgesInto(SEdgeList *sel, int how, } } +void SKdNode::MakeOutlinesInto(SOutlineList *sol) +{ + std::vector tris; + ClearTags(); + ListTrianglesInto(&tris); + + std::set> edgeTris; + int cnt = 1234; + for(STriangle *tr : tris) { + for(int j = 0; j < 3; j++) { + Vector a = (j == 0) ? tr->a : ((j == 1) ? tr->b : tr->c); + Vector b = (j == 0) ? tr->b : ((j == 1) ? tr->c : tr->a); + + SKdNode::EdgeOnInfo info = {}; + FindEdgeOn(a, b, cnt, /*coplanarIsInter=*/false, &info); + cnt++; + if(info.count != 1) continue; + if(CheckAndAddTrianglePair(&edgeTris, tr, info.tr)) + continue; + + sol->AddEdge(a, b, tr->Normal(), info.tr->Normal()); + } + } +} + +void SOutlineList::Clear() { + l.Clear(); +} + +void SOutlineList::AddEdge(Vector a, Vector b, Vector nl, Vector nr) { + SOutline so = {}; + so.a = a; + so.b = b; + so.nl = nl; + so.nr = nr; + l.Add(&so); +} + +void SOutlineList::FillOutlineTags(Vector projDir) { + for(SOutline *so = l.First(); so; so = l.NextAfter(so)) { + so->tag = ((so->nl.Dot(projDir) > 0.0) != (so->nr.Dot(projDir) > 0.0)); + } +} + +void SOutlineList::MakeFromCopyOf(SOutlineList *sol) { + for(SOutline *so = sol->l.First(); so; so = sol->l.NextAfter(so)) { + l.Add(so); + } +} diff --git a/src/polygon.h b/src/polygon.h index 4249e7a..7d2907b 100644 --- a/src/polygon.h +++ b/src/polygon.h @@ -13,6 +13,7 @@ class SPolygon; class SContour; class SMesh; class SBsp3; +class SOutlineList; class SEdge { public: @@ -249,7 +250,7 @@ public: void MakeFromAssemblyOf(SMesh *a, SMesh *b); void MakeEdgesInPlaneInto(SEdgeList *sel, Vector n, double d); - void MakeCertainEdgesInto(SEdgeList *sel, int type); + void MakeCertainEdgesAndOutlinesInto(SEdgeList *sel, SOutlineList *sol, int type); bool IsEmpty(void); void RemapFaces(Group *g, int remap); @@ -267,6 +268,24 @@ public: static STriangleLl *Alloc(void); }; +class SOutline { +public: + int tag; + Vector a, b, nl, nr; +}; + +class SOutlineList { +public: + List l; + + void Clear(); + void AddEdge(Vector a, Vector b, Vector nl, Vector nr); + + void MakeFromCopyOf(SOutlineList *ol); + + void FillOutlineTags(Vector projDir); +}; + class SKdNode { public: struct EdgeOnInfo { @@ -304,7 +323,8 @@ public: SHARP_EDGES = 500, }; void MakeCertainEdgesInto(SEdgeList *sel, int how, bool coplanarIsInter, - bool *inter, bool *leaky); + bool *inter, bool *leaky, int auxA=0); + void MakeOutlinesInto(SOutlineList *sel); void OcclusionTestLine(SEdge orig, SEdgeList *sel, int cnt, bool removeHidden); void SplitLinesAgainstTriangle(SEdgeList *sel, STriangle *tr, bool removeHidden); diff --git a/src/sketch.h b/src/sketch.h index e1c31aa..b34aeb5 100644 --- a/src/sketch.h +++ b/src/sketch.h @@ -176,6 +176,7 @@ public: bool displayDirty; SMesh displayMesh; SEdgeList displayEdges; + SOutlineList displayOutlines; enum { COMBINE_AS_UNION = 0, @@ -768,6 +769,7 @@ public: DRAW_ERROR = 12, DIM_SOLID = 13, HIDDEN_EDGE = 14, + OUTLINE = 15, FIRST_CUSTOM = 0x100 }; diff --git a/src/solvespace.h b/src/solvespace.h index 729c708..83939e3 100644 --- a/src/solvespace.h +++ b/src/solvespace.h @@ -21,6 +21,7 @@ #include #include #include +#include #ifdef HAVE_STDINT_H # include #endif @@ -346,6 +347,7 @@ void ssglFillMesh(bool useSpecColor, RgbaColor color, SMesh *m, uint32_t h, uint32_t s1, uint32_t s2); void ssglDebugPolygon(SPolygon *p); void ssglDrawEdges(SEdgeList *l, bool endpointsToo, hStyle hs); +void ssglDrawOutlines(SOutlineList *l, Vector projDir, hStyle hs); void ssglDebugMesh(SMesh *m); void ssglMarkPolygonNormal(SPolygon *p); typedef void ssglLineFn(void *data, Vector a, Vector b); diff --git a/src/style.cpp b/src/style.cpp index 24b1998..1fde1de 100644 --- a/src/style.cpp +++ b/src/style.cpp @@ -25,6 +25,7 @@ const Style::Default Style::Defaults[] = { { { DRAW_ERROR }, "DrawError", RGBf(1.0, 0.0, 0.0), 8.0, 0 }, { { DIM_SOLID }, "DimSolid", RGBf(0.1, 0.1, 0.1), 1.0, 0 }, { { HIDDEN_EDGE }, "HiddenEdge", RGBf(0.8, 0.8, 0.8), 2.0, 1 }, + { { OUTLINE }, "Outline", RGBf(0.8, 0.8, 0.8), 3.0, 5 }, { { 0 }, NULL, RGBf(0.0, 0.0, 0.0), 0.0, 0 } }; diff --git a/src/textwin.cpp b/src/textwin.cpp index f5d6b51..e685869 100644 --- a/src/textwin.cpp +++ b/src/textwin.cpp @@ -38,6 +38,7 @@ TextWindow::HideShowIcon TextWindow::hideShowIcons[] = { { &SPACER, 0, 0 }, { &(SS.GW.showShaded), Icon_shaded, "shaded view of solid model" }, { &(SS.GW.showEdges), Icon_edges, "edges of solid model" }, + { &(SS.GW.showOutlines), Icon_outlines, "outline of solid model" }, { &(SS.GW.showMesh), Icon_mesh, "triangle mesh of solid model" }, { &SPACER, 0, 0 }, { &(SS.GW.showHdnLines), Icon_hidden_lines, "hidden lines" }, diff --git a/src/ui.h b/src/ui.h index cdf2662..9762f31 100644 --- a/src/ui.h +++ b/src/ui.h @@ -704,6 +704,7 @@ public: bool showTextWindow; bool showShaded; bool showEdges; + bool showOutlines; bool showFaces; bool showMesh; bool showHdnLines; diff --git a/src/undoredo.cpp b/src/undoredo.cpp index 3bdd1a8..04b96ee 100644 --- a/src/undoredo.cpp +++ b/src/undoredo.cpp @@ -64,6 +64,7 @@ void SolveSpaceUI::PushFromCurrentOnto(UndoStack *uk) { dest.runningShell = {}; dest.displayMesh = {}; dest.displayEdges = {}; + dest.displayOutlines = {}; dest.remap = {}; src->remap.DeepCopyInto(&(dest.remap));