diff --git a/src/draw.cpp b/src/draw.cpp index 9652d55..268e5a4 100644 --- a/src/draw.cpp +++ b/src/draw.cpp @@ -34,7 +34,7 @@ void GraphicsWindow::Selection::Draw(void) { Vector refp = Vector::From(0, 0, 0); if(entity.v) { Entity *e = SK.GetEntity(entity); - e->Draw(); + e->Draw(/*drawAsHidden=*/false); if(emphasized) refp = e->GetReferencePos(); } if(constraint.v) { @@ -686,9 +686,15 @@ nogrid:; // Draw the active group; this does stuff like the mesh and edges. (SK.GetGroup(activeGroup))->Draw(); - // Now draw the entities - if(showHdnLines) glDisable(GL_DEPTH_TEST); - Entity::DrawAll(); + // Now draw the entities. + if(SS.GW.showHdnLines) { + ssglDepthRangeOffset(2); + glDepthFunc(GL_GREATER); + Entity::DrawAll(/*drawAsHidden=*/true); + glDepthFunc(GL_LEQUAL); + } + ssglDepthRangeOffset(0); + Entity::DrawAll(/*drawAsHidden=*/false); // Draw filled paths in all groups, when those filled paths were requested // specially by assigning a style with a fill color, or when the filled @@ -737,9 +743,7 @@ nogrid:; glEnd(); // And the naked edges, if the user did Analyze -> Show Naked Edges. - ssglLineWidth(Style::Width(Style::DRAW_ERROR)); - ssglColorRGB(Style::Color(Style::DRAW_ERROR)); - ssglDrawEdges(&(SS.nakedEdges), true); + ssglDrawEdges(&(SS.nakedEdges), true, { Style::DRAW_ERROR }); // Then redraw whatever the mouse is hovering over, highlighted. glDisable(GL_DEPTH_TEST); diff --git a/src/drawentity.cpp b/src/drawentity.cpp index 6aab2b9..82dce99 100644 --- a/src/drawentity.cpp +++ b/src/drawentity.cpp @@ -40,10 +40,9 @@ void Entity::LineDrawOrGetDistance(Vector a, Vector b, bool maybeFat, int data) dogd.refp = (a.Plus(b)).ScaledBy(0.5); } -void Entity::DrawAll(void) { - // This handles points and line segments as a special case, because I - // seem to be able to get a huge speedup that way, by consolidating - // stuff to gl. +void Entity::DrawAll(bool drawAsHidden) { + // This handles points as a special case, because I seem to be able + // to get a huge speedup that way, by consolidating stuff to gl. int i; if(SS.GW.showPoints) { double s = 3.5/SS.GW.scale; @@ -98,19 +97,23 @@ void Entity::DrawAll(void) { for(i = 0; i < SK.entity.n; i++) { Entity *e = &(SK.entity.elem[i]); - if(e->IsPoint()) - { + if(e->IsPoint()) { continue; // already handled } - e->Draw(); + e->Draw(drawAsHidden); } } -void Entity::Draw(void) { +void Entity::Draw(bool drawAsHidden) { hStyle hs = Style::ForEntity(h); dogd.lineWidth = Style::Width(hs); - dogd.stippleType = Style::PatternType(hs); - dogd.stippleScale = Style::StippleScaleMm(hs); + if(drawAsHidden) { + dogd.stippleType = Style::PatternType({ Style::HIDDEN_EDGE }); + dogd.stippleScale = Style::StippleScaleMm({ Style::HIDDEN_EDGE }); + } else { + dogd.stippleType = Style::PatternType(hs); + dogd.stippleScale = Style::StippleScaleMm(hs); + } ssglLineWidth((float)dogd.lineWidth); ssglColorRGB(Style::Color(hs)); diff --git a/src/export.cpp b/src/export.cpp index ea7d599..1e39627 100644 --- a/src/export.cpp +++ b/src/export.cpp @@ -122,7 +122,7 @@ void SolveSpaceUI::ExportViewOrWireframeTo(const std::string &filename, bool wir GenerateAll(GENERATE_ALL); SMesh *sm = NULL; - if(SS.GW.showShaded) { + if(SS.GW.showShaded || SS.GW.showHdnLines) { Group *g = SK.GetGroup(SS.GW.activeGroup); g->GenerateDisplayItems(); sm = &(g->displayMesh); @@ -136,8 +136,7 @@ void SolveSpaceUI::ExportViewOrWireframeTo(const std::string &filename, bool wir if(!e->IsVisible()) continue; if(e->construction) continue; - if(SS.exportPwlCurves || (sm && !SS.GW.showHdnLines) || - fabs(SS.exportOffset) > LENGTH_EPS) + if(SS.exportPwlCurves || sm || fabs(SS.exportOffset) > LENGTH_EPS) { // We will be doing hidden line removal, which we can't do on // exact curves; so we need things broken down to pwls. Same @@ -322,7 +321,7 @@ void SolveSpaceUI::ExportLinesAndMesh(SEdgeList *sel, SBezierList *sbl, SMesh *s // And now we perform hidden line removal if requested SEdgeList hlrd = {}; - if(sm && !SS.GW.showHdnLines) { + if(sm) { SKdNode *root = SKdNode::From(&smp); // Generate the edges where a curved surface turns from front-facing @@ -344,19 +343,19 @@ void SolveSpaceUI::ExportLinesAndMesh(SEdgeList *sel, SBezierList *sbl, SMesh *s continue; } - SEdgeList out = {}; + SEdgeList edges = {}; // Split the original edge against the mesh - out.AddEdge(se->a, se->b, se->auxA); - root->OcclusionTestLine(*se, &out, cnt); + edges.AddEdge(se->a, se->b, se->auxA); + root->OcclusionTestLine(*se, &edges, cnt, /*removeHidden=*/!SS.GW.showHdnLines); // the occlusion test splits unnecessarily; so fix those - out.MergeCollinearSegments(se->a, se->b); + edges.MergeCollinearSegments(se->a, se->b); cnt++; // And add the results to our output SEdge *sen; - for(sen = out.l.First(); sen; sen = out.l.NextAfter(sen)) { + for(sen = edges.l.First(); sen; sen = edges.l.NextAfter(sen)) { hlrd.AddEdge(sen->a, sen->b, sen->auxA); } - out.Clear(); + edges.Clear(); } sel = &hlrd; @@ -516,6 +515,12 @@ void SolveSpaceUI::ExportLinesAndMesh(SEdgeList *sel, SBezierList *sbl, SMesh *s sblss.AddOpenPath(b); } + // We need the mesh for occlusion testing, but if we don't export it, + // erase it now. + if(!SS.GW.showShaded) { + sms.Clear(); + } + // Now write the lines and triangles to the output file out->OutputLinesAndMesh(&sblss, &sms); diff --git a/src/glhelper.cpp b/src/glhelper.cpp index d3406e1..8f706ee 100644 --- a/src/glhelper.cpp +++ b/src/glhelper.cpp @@ -562,15 +562,19 @@ void ssglDebugPolygon(SPolygon *p) } } -void ssglDrawEdges(SEdgeList *el, bool endpointsToo) +void ssglDrawEdges(SEdgeList *el, bool endpointsToo, hStyle hs) { + double lineWidth = Style::Width(hs); + int stippleType = Style::PatternType(hs); + double stippleScale = Style::StippleScaleMm(hs); + ssglLineWidth(float(lineWidth)); + ssglColorRGB(Style::Color(hs)); + SEdge *se; - glBegin(GL_LINES); for(se = el->l.First(); se; se = el->l.NextAfter(se)) { - ssglVertex3v(se->a); - ssglVertex3v(se->b); + ssglStippledLine(se->a, se->b, lineWidth, stippleType, stippleScale, + /*maybeFat=*/true); } - glEnd(); if(endpointsToo) { glPointSize(12); diff --git a/src/groupmesh.cpp b/src/groupmesh.cpp index 35d9684..8889b24 100644 --- a/src/groupmesh.cpp +++ b/src/groupmesh.cpp @@ -476,7 +476,7 @@ void Group::DrawDisplayItems(int t) { if(gs.faces > 0) ms1 = gs.face[0].v; if(gs.faces > 1) ms2 = gs.face[1].v; - if(SS.GW.showShaded) { + if(SS.GW.showShaded || SS.GW.showHdnLines) { if(SS.drawBackFaces && !displayMesh.isTransparent) { // For debugging, draw the backs of the triangles in red, so that we // notice when a shell is open @@ -485,16 +485,26 @@ void Group::DrawDisplayItems(int t) { glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, 0); } + // Draw the shaded solid into the depth buffer for hidden line removal, + // and if we're actually going to display it, to the color buffer too. glEnable(GL_LIGHTING); + if(!SS.GW.showShaded) glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); ssglFillMesh(useSpecColor, specColor, &displayMesh, mh, ms1, ms2); + if(!SS.GW.showShaded) glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); glDisable(GL_LIGHTING); } if(SS.GW.showEdges) { + glDepthMask(GL_FALSE); + if(SS.GW.showHdnLines) { + ssglDepthRangeOffset(0); + glDepthFunc(GL_GREATER); + ssglDrawEdges(&displayEdges, false, { Style::HIDDEN_EDGE }); + glDepthFunc(GL_LEQUAL); + } ssglDepthRangeOffset(2); - ssglColorRGB(Style::Color(Style::SOLID_EDGE)); - ssglLineWidth(Style::Width(Style::SOLID_EDGE)); - ssglDrawEdges(&displayEdges, false); + ssglDrawEdges(&displayEdges, false, { Style::SOLID_EDGE }); + glDepthMask(GL_TRUE); } if(SS.GW.showMesh) ssglDebugMesh(&displayMesh); diff --git a/src/icons/hidden-lines.png b/src/icons/hidden-lines.png index cf43afb..35ea04b 100644 Binary files a/src/icons/hidden-lines.png and b/src/icons/hidden-lines.png differ diff --git a/src/mesh.cpp b/src/mesh.cpp index 51fa0d3..b7f14d1 100644 --- a/src/mesh.cpp +++ b/src/mesh.cpp @@ -653,7 +653,7 @@ void SKdNode::SnapToMesh(SMesh *m) { // them for occlusion. Keep only the visible segments. sel is both our input // and our output. //----------------------------------------------------------------------------- -void SKdNode::SplitLinesAgainstTriangle(SEdgeList *sel, STriangle *tr) { +void SKdNode::SplitLinesAgainstTriangle(SEdgeList *sel, STriangle *tr, bool removeHidden) { SEdgeList seln = {}; Vector tn = tr->Normal().WithMagnitude(1); @@ -754,8 +754,11 @@ void SKdNode::SplitLinesAgainstTriangle(SEdgeList *sel, STriangle *tr) { if(n[i].Dot(pt) - d[i] > LENGTH_EPS) se->tag = 0; } } + if(!removeHidden && se->tag == 1) + se->auxA = Style::HIDDEN_EDGE; } - sel->l.RemoveTagged(); + if(removeHidden) + sel->l.RemoveTagged(); } } @@ -763,7 +766,7 @@ void SKdNode::SplitLinesAgainstTriangle(SEdgeList *sel, STriangle *tr) { // Given an edge orig, occlusion test it against our mesh. We output an edge // list in sel, containing the visible portions of that edge. //----------------------------------------------------------------------------- -void SKdNode::OcclusionTestLine(SEdge orig, SEdgeList *sel, int cnt) { +void SKdNode::OcclusionTestLine(SEdge orig, SEdgeList *sel, int cnt, bool removeHidden) { if(gt && lt) { double ac = (orig.a).Element(which), bc = (orig.b).Element(which); @@ -773,13 +776,13 @@ void SKdNode::OcclusionTestLine(SEdge orig, SEdgeList *sel, int cnt) { bc < c + KDTREE_EPS || which == 2) { - lt->OcclusionTestLine(orig, sel, cnt); + lt->OcclusionTestLine(orig, sel, cnt, removeHidden); } if(ac > c - KDTREE_EPS || bc > c - KDTREE_EPS || which == 2) { - gt->OcclusionTestLine(orig, sel, cnt); + gt->OcclusionTestLine(orig, sel, cnt, removeHidden); } } else { STriangleLl *ll; @@ -788,7 +791,7 @@ void SKdNode::OcclusionTestLine(SEdge orig, SEdgeList *sel, int cnt) { if(tr->tag == cnt) continue; - SplitLinesAgainstTriangle(sel, tr); + SplitLinesAgainstTriangle(sel, tr, removeHidden); tr->tag = cnt; } } diff --git a/src/polygon.cpp b/src/polygon.cpp index 2bedbbe..7e1cfb7 100644 --- a/src/polygon.cpp +++ b/src/polygon.cpp @@ -453,7 +453,7 @@ void SEdgeList::MergeCollinearSegments(Vector a, Vector b) { SEdge *prev = &(l.elem[i-1]), *now = &(l.elem[i]); - if((prev->b).Equals(now->a)) { + if((prev->b).Equals(now->a) && prev->auxA == now->auxA) { // The previous segment joins up to us; so merge it into us. prev->tag = 1; now->a = prev->a; diff --git a/src/polygon.h b/src/polygon.h index 930994e..4249e7a 100644 --- a/src/polygon.h +++ b/src/polygon.h @@ -306,8 +306,8 @@ public: void MakeCertainEdgesInto(SEdgeList *sel, int how, bool coplanarIsInter, bool *inter, bool *leaky); - void OcclusionTestLine(SEdge orig, SEdgeList *sel, int cnt); - void SplitLinesAgainstTriangle(SEdgeList *sel, STriangle *tr); + void OcclusionTestLine(SEdge orig, SEdgeList *sel, int cnt, bool removeHidden); + void SplitLinesAgainstTriangle(SEdgeList *sel, STriangle *tr, bool removeHidden); void SnapToMesh(SMesh *m); void SnapToVertex(Vector v, SMesh *extras); diff --git a/src/sketch.h b/src/sketch.h index e700c48..e1c31aa 100644 --- a/src/sketch.h +++ b/src/sketch.h @@ -505,8 +505,8 @@ public: void GenerateBezierCurves(SBezierList *sbl); void GenerateEdges(SEdgeList *el, bool includingConstruction=false); - static void DrawAll(void); - void Draw(void); + static void DrawAll(bool drawAsHidden); + void Draw(bool drawAsHidden); double GetDistance(Point2d mp); Vector GetReferencePos(void); @@ -767,6 +767,7 @@ public: ANALYZE = 11, DRAW_ERROR = 12, DIM_SOLID = 13, + HIDDEN_EDGE = 14, FIRST_CUSTOM = 0x100 }; diff --git a/src/solvespace.h b/src/solvespace.h index a3fa2a9..729c708 100644 --- a/src/solvespace.h +++ b/src/solvespace.h @@ -345,7 +345,7 @@ void ssglFillPolygon(SPolygon *p); 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); +void ssglDrawEdges(SEdgeList *l, bool endpointsToo, 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 f7d8622..24b1998 100644 --- a/src/style.cpp +++ b/src/style.cpp @@ -24,6 +24,7 @@ const Style::Default Style::Defaults[] = { { { ANALYZE }, "Analyze", RGBf(0.0, 1.0, 1.0), 1.0, 0 }, { { 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 }, { { 0 }, NULL, RGBf(0.0, 0.0, 0.0), 0.0, 0 } }; @@ -94,7 +95,8 @@ void Style::FillDefaultStyle(Style *s, const Default *d) { s->exportable = true; s->filled = false; s->fillColor = RGBf(0.3, 0.3, 0.3); - s->stippleType = Style::STIPPLE_CONTINUOUS; + s->stippleType = (d->h.v == Style::HIDDEN_EDGE) ? Style::STIPPLE_DASH + : Style::STIPPLE_CONTINUOUS; s->stippleScale = 15.0; s->zIndex = d->zIndex; } @@ -828,43 +830,41 @@ void TextWindow::ShowStyleInfo(void) { SS.UnitName()); } - if(s->h.v >= Style::FIRST_CUSTOM) { - Printf(false,"%Ba %Ftstipple type:%E"); + Printf(false,"%Ba %Ftstipple type:%E"); - const size_t patternCount = Style::LAST_STIPPLE + 1; - const char *patternsSource[patternCount] = { - "___________", - "- - - - - -", - "__ __ __ __", - "-.-.-.-.-.-", - "..-..-..-..", - "...........", - "~~~~~~~~~~~", - "__~__~__~__" - }; - std::string patterns[patternCount]; + const size_t patternCount = Style::LAST_STIPPLE + 1; + const char *patternsSource[patternCount] = { + "___________", + "- - - - - -", + "__ __ __ __", + "-.-.-.-.-.-", + "..-..-..-..", + "...........", + "~~~~~~~~~~~", + "__~__~__~__" + }; + std::string patterns[patternCount]; - for(int i = 0; i <= Style::LAST_STIPPLE; i++) { - const char *str = patternsSource[i]; - do { - switch(*str) { - case ' ': patterns[i] += " "; break; - case '.': patterns[i] += "\xEE\x80\x84"; break; - case '_': patterns[i] += "\xEE\x80\x85"; break; - case '-': patterns[i] += "\xEE\x80\x86"; break; - case '~': patterns[i] += "\xEE\x80\x87"; break; - default: oops(); - } - } while(*(++str)); - } + for(int i = 0; i <= Style::LAST_STIPPLE; i++) { + const char *str = patternsSource[i]; + do { + switch(*str) { + case ' ': patterns[i] += " "; break; + case '.': patterns[i] += "\xEE\x80\x84"; break; + case '_': patterns[i] += "\xEE\x80\x85"; break; + case '-': patterns[i] += "\xEE\x80\x86"; break; + case '~': patterns[i] += "\xEE\x80\x87"; break; + default: oops(); + } + } while(*(++str)); + } - for(int i = 0; i <= Style::LAST_STIPPLE; i++) { - const char *radio = s->stippleType == i ? RADIO_TRUE : RADIO_FALSE; - Printf(false, "%Bp %D%f%Lp%s %s%E", - (i % 2 == 0) ? 'd' : 'a', - s->h.v, &ScreenChangeStylePatternType, - i + 1, radio, patterns[i].c_str()); - } + for(int i = 0; i <= Style::LAST_STIPPLE; i++) { + const char *radio = s->stippleType == i ? RADIO_TRUE : RADIO_FALSE; + Printf(false, "%Bp %D%f%Lp%s %s%E", + (i % 2 == 0) ? 'd' : 'a', + s->h.v, &ScreenChangeStylePatternType, + i + 1, radio, patterns[i].c_str()); } if(s->h.v >= Style::FIRST_CUSTOM) {