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.
pull/4/head
EvilSpirit 2016-03-14 22:14:24 +06:00 committed by whitequark
parent d1a2eb6d18
commit 24fc65a71c
14 changed files with 128 additions and 18 deletions

View File

@ -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();

View File

@ -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;

View File

@ -233,6 +233,7 @@ void GraphicsWindow::Init(void) {
showShaded = true;
showEdges = true;
showMesh = false;
showOutlines = false;
showTextWindow = true;
ShowTextWindow(showTextWindow);

View File

@ -29,6 +29,7 @@ void Group::Clear(void) {
runningShell.Clear();
displayMesh.Clear();
displayEdges.Clear();
displayOutlines.Clear();
impMesh.Clear();
impShell.Clear();
impEntity.Clear();

View File

@ -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);
}

BIN
src/icons/outlines.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 930 B

View File

@ -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<std::pair<STriangle *, STriangle *>
// * 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<STriangle *> tris;
ClearTags();
ListTrianglesInto(&tris);
std::set<std::pair<STriangle *, STriangle *>> 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);
}
}

View File

@ -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<SOutline> 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);

View File

@ -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
};

View File

@ -21,6 +21,7 @@
#include <string>
#include <vector>
#include <map>
#include <set>
#ifdef HAVE_STDINT_H
# include <stdint.h>
#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);

View File

@ -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 }
};

View File

@ -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" },

View File

@ -704,6 +704,7 @@ public:
bool showTextWindow;
bool showShaded;
bool showEdges;
bool showOutlines;
bool showFaces;
bool showMesh;
bool showHdnLines;

View File

@ -64,6 +64,7 @@ void SolveSpaceUI::PushFromCurrentOnto(UndoStack *uk) {
dest.runningShell = {};
dest.displayMesh = {};
dest.displayEdges = {};
dest.displayOutlines = {};
dest.remap = {};
src->remap.DeepCopyInto(&(dest.remap));