Put back code to generate emphasized edges from a mesh; so now we
can show edges for both meshes and shells, and export them and hidden line remove and all the usual stuff. And fix the zoom to fit on startup, so that it considers hidden entities too. That avoids the problem where things get generated at stupid chord tolerance because no entities were visible and the mesh of course did not yet exist. [git-p4: depot-paths = "//depot/solvespace/": change = 1961]solver
parent
7536ccb054
commit
842645d61f
|
@ -248,7 +248,8 @@ void SolveSpace::ExportLinesAndMesh(SEdgeList *sel, SBezierList *sbl, SMesh *sm,
|
|||
// Generate the edges where a curved surface turns from front-facing
|
||||
// to back-facing.
|
||||
if(SS.GW.showEdges) {
|
||||
root->MakeTurningEdgesInto(sel);
|
||||
root->MakeCertainEdgesInto(sel, SKdNode::TURNING_EDGES,
|
||||
false, NULL, NULL);
|
||||
}
|
||||
|
||||
root->ClearTags();
|
||||
|
@ -864,8 +865,9 @@ void SvgFileWriter::StartFile(void) {
|
|||
"\r\n"
|
||||
"<title>Exported SVG</title>\r\n"
|
||||
"\r\n",
|
||||
ptMax.x - ptMin.x, ptMax.y - ptMin.y,
|
||||
ptMax.x - ptMin.x, ptMax.y - ptMin.y);
|
||||
(ptMax.x - ptMin.x) + 1, (ptMax.y - ptMin.y) + 1,
|
||||
(ptMax.x - ptMin.x) + 1, (ptMax.y - ptMin.y) + 1);
|
||||
// A little bit of extra space for the stroke width.
|
||||
}
|
||||
|
||||
void SvgFileWriter::LineSegment(double x0, double y0, double x1, double y1) {
|
||||
|
|
|
@ -252,6 +252,9 @@ void GraphicsWindow::HandlePointForZoomToFit(Vector p,
|
|||
{
|
||||
double w;
|
||||
Vector pp = ProjectPoint4(p, &w);
|
||||
// If div is true, then we calculate a perspective projection of the point.
|
||||
// If not, then we do a parallel projection regardless of the current
|
||||
// scale factor.
|
||||
if(div) {
|
||||
pp = pp.ScaledBy(1.0/w);
|
||||
}
|
||||
|
@ -262,15 +265,15 @@ void GraphicsWindow::HandlePointForZoomToFit(Vector p,
|
|||
pmin->y = min(pmin->y, pp.y);
|
||||
*wmin = min(*wmin, w);
|
||||
}
|
||||
void GraphicsWindow::LoopOverPoints(
|
||||
Point2d *pmax, Point2d *pmin, double *wmin, bool div)
|
||||
void GraphicsWindow::LoopOverPoints(Point2d *pmax, Point2d *pmin, double *wmin,
|
||||
bool div, bool includingInvisibles)
|
||||
{
|
||||
HandlePointForZoomToFit(Vector::From(0, 0, 0), pmax, pmin, wmin, div);
|
||||
|
||||
int i, j;
|
||||
for(i = 0; i < SK.entity.n; i++) {
|
||||
Entity *e = &(SK.entity.elem[i]);
|
||||
if(!e->IsVisible()) continue;
|
||||
if(!(e->IsVisible() || includingInvisibles)) continue;
|
||||
if(e->IsPoint()) {
|
||||
HandlePointForZoomToFit(e->PointGetNum(), pmax, pmin, wmin, div);
|
||||
} else if(e->type == Entity::CIRCLE) {
|
||||
|
@ -306,11 +309,11 @@ void GraphicsWindow::LoopOverPoints(
|
|||
}
|
||||
}
|
||||
}
|
||||
void GraphicsWindow::ZoomToFit(void) {
|
||||
void GraphicsWindow::ZoomToFit(bool includingInvisibles) {
|
||||
// On the first run, ignore perspective.
|
||||
Point2d pmax = { -1e12, -1e12 }, pmin = { 1e12, 1e12 };
|
||||
double wmin = 1;
|
||||
LoopOverPoints(&pmax, &pmin, &wmin, false);
|
||||
LoopOverPoints(&pmax, &pmin, &wmin, false, includingInvisibles);
|
||||
|
||||
double xm = (pmax.x + pmin.x)/2, ym = (pmax.y + pmin.y)/2;
|
||||
double dx = pmax.x - pmin.x, dy = pmax.y - pmin.y;
|
||||
|
@ -335,7 +338,7 @@ void GraphicsWindow::ZoomToFit(void) {
|
|||
pmax.x = -1e12; pmax.y = -1e12;
|
||||
pmin.x = 1e12; pmin.y = 1e12;
|
||||
wmin = 1;
|
||||
LoopOverPoints(&pmax, &pmin, &wmin, true);
|
||||
LoopOverPoints(&pmax, &pmin, &wmin, true, includingInvisibles);
|
||||
|
||||
// Adjust the scale so that no points are behind the camera
|
||||
if(wmin < 0.1) {
|
||||
|
@ -359,7 +362,7 @@ void GraphicsWindow::MenuView(int id) {
|
|||
break;
|
||||
|
||||
case MNU_ZOOM_TO_FIT:
|
||||
SS.GW.ZoomToFit();
|
||||
SS.GW.ZoomToFit(false);
|
||||
break;
|
||||
|
||||
case MNU_NEAREST_ORTHO:
|
||||
|
@ -718,6 +721,10 @@ void GraphicsWindow::ToggleBool(int link, DWORD v) {
|
|||
// so not meaningful to show them and hide the shaded.
|
||||
if(!SS.GW.showShaded) SS.GW.showFaces = false;
|
||||
|
||||
// We might need to regenerate the mesh and edge list, since the edges
|
||||
// wouldn't have been generated if they were previously hidden.
|
||||
if(SS.GW.showEdges) (SK.GetGroup(SS.GW.activeGroup))->displayDirty = true;
|
||||
|
||||
SS.GenerateAll();
|
||||
InvalidateGraphics();
|
||||
SS.later.showTW = true;
|
||||
|
|
|
@ -321,6 +321,9 @@ void Group::GenerateShellAndMesh(void) {
|
|||
}
|
||||
|
||||
void Group::GenerateDisplayItems(void) {
|
||||
// This is potentially slow (since we've got to triangulate a shell, or
|
||||
// to find the emphasized edges for a mesh), so we will run it only
|
||||
// if its inputs have changed.
|
||||
if(displayDirty) {
|
||||
displayMesh.Clear();
|
||||
runningShell.TriangulateInto(&displayMesh);
|
||||
|
@ -335,7 +338,11 @@ void Group::GenerateDisplayItems(void) {
|
|||
}
|
||||
|
||||
displayEdges.Clear();
|
||||
runningShell.MakeEdgesInto(&displayEdges);
|
||||
|
||||
if(SS.GW.showEdges) {
|
||||
runningShell.MakeEdgesInto(&displayEdges);
|
||||
displayMesh.MakeEmphasizedEdgesInto(&displayEdges);
|
||||
}
|
||||
|
||||
displayDirty = false;
|
||||
}
|
||||
|
|
131
mesh.cpp
131
mesh.cpp
|
@ -77,11 +77,28 @@ void SMesh::MakeEdgesInPlaneInto(SEdgeList *sel, Vector n, double d) {
|
|||
// Select the naked edges in our resulting open mesh.
|
||||
SKdNode *root = SKdNode::From(&m);
|
||||
root->SnapToMesh(&m);
|
||||
root->MakeNakedEdgesInto(sel, false, NULL, NULL);
|
||||
root->MakeCertainEdgesInto(sel, SKdNode::NAKED_OR_SELF_INTER_EDGES,
|
||||
false, NULL, NULL);
|
||||
|
||||
m.Clear();
|
||||
}
|
||||
|
||||
void SMesh::MakeEmphasizedEdgesInto(SEdgeList *sel) {
|
||||
SKdNode *root = SKdNode::From(this);
|
||||
root->MakeCertainEdgesInto(sel, SKdNode::EMPHASIZED_EDGES,
|
||||
false, NULL, NULL);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// When we are called, all of the triangles from l.elem[start] to the end must
|
||||
// be coplanar. So we try to find a set of fewer triangles that covers the
|
||||
// exact same area, in order to reduce the number of triangles in the mesh.
|
||||
// We use this after a triangle has been split against the BSP.
|
||||
//
|
||||
// This is really ugly code; basically it just pastes things together to
|
||||
// form convex polygons, merging collinear edges when possible, then
|
||||
// triangulates the convex poly.
|
||||
//-----------------------------------------------------------------------------
|
||||
void SMesh::Simplify(int start) {
|
||||
int maxTriangles = (l.n - start) + 10;
|
||||
|
||||
|
@ -756,7 +773,8 @@ void SKdNode::OcclusionTestLine(SEdge orig, SEdgeList *sel, int cnt) {
|
|||
// mesh, otherwise not.
|
||||
//-----------------------------------------------------------------------------
|
||||
void SKdNode::FindEdgeOn(Vector a, Vector b, int *n, int cnt,
|
||||
bool coplanarIsInter, bool *inter, bool *fwd)
|
||||
bool coplanarIsInter, bool *inter, bool *fwd,
|
||||
DWORD *face)
|
||||
{
|
||||
if(gt && lt) {
|
||||
double ac = a.Element(which),
|
||||
|
@ -764,12 +782,12 @@ void SKdNode::FindEdgeOn(Vector a, Vector b, int *n, int cnt,
|
|||
if(ac < c + KDTREE_EPS ||
|
||||
bc < c + KDTREE_EPS)
|
||||
{
|
||||
lt->FindEdgeOn(a, b, n, cnt, coplanarIsInter, inter, fwd);
|
||||
lt->FindEdgeOn(a, b, n, cnt, coplanarIsInter, inter, fwd, face);
|
||||
}
|
||||
if(ac > c - KDTREE_EPS ||
|
||||
bc > c - KDTREE_EPS)
|
||||
{
|
||||
gt->FindEdgeOn(a, b, n, cnt, coplanarIsInter, inter, fwd);
|
||||
gt->FindEdgeOn(a, b, n, cnt, coplanarIsInter, inter, fwd, face);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -794,6 +812,8 @@ void SKdNode::FindEdgeOn(Vector a, Vector b, int *n, int cnt,
|
|||
} else {
|
||||
*fwd = false;
|
||||
}
|
||||
// And record the triangle's face
|
||||
*face = tr->meta.face;
|
||||
} else if(((a.Equals(tr->a) && b.Equals(tr->b)) ||
|
||||
(a.Equals(tr->b) && b.Equals(tr->c)) ||
|
||||
(a.Equals(tr->c) && b.Equals(tr->a))))
|
||||
|
@ -848,15 +868,16 @@ void SKdNode::FindEdgeOn(Vector a, Vector b, int *n, int cnt,
|
|||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Report all naked edges of the mesh (i.e., edges that don't join up to
|
||||
// a single anti-parallel edge of another triangle), and all edges that
|
||||
// intersect another triangle. If coplanarIsInter, then edges coplanar with
|
||||
// another triangle and within it are reported, otherwise not. We report
|
||||
// in *inter and *leaky whether the mesh is self-intersecting or leaky
|
||||
// (having naked edges) respectively.
|
||||
// Pick certain classes of edges out from our mesh. These might be:
|
||||
// * naked edges (i.e., edges with no anti-parallel neighbor) and self-
|
||||
// intersecting edges (i.e., edges that cross another triangle)
|
||||
// * turning edges (i.e., edges where a front-facing triangle joins
|
||||
// a back-facing triangle)
|
||||
// * emphasized edges (i.e., edges where a triangle from one face joins
|
||||
// a triangle from a different face)
|
||||
//-----------------------------------------------------------------------------
|
||||
void SKdNode::MakeNakedEdgesInto(SEdgeList *sel, bool coplanarIsInter,
|
||||
bool *inter, bool *leaky)
|
||||
void SKdNode::MakeCertainEdgesInto(SEdgeList *sel, int how,
|
||||
bool coplanarIsInter, bool *inter, bool *leaky)
|
||||
{
|
||||
if(inter) *inter = false;
|
||||
if(leaky) *leaky = false;
|
||||
|
@ -871,57 +892,51 @@ void SKdNode::MakeNakedEdgesInto(SEdgeList *sel, bool coplanarIsInter,
|
|||
for(i = 0; i < m.l.n; i++) {
|
||||
STriangle *tr = &(m.l.elem[i]);
|
||||
|
||||
for(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);
|
||||
|
||||
int n = 0, nOther = 0;
|
||||
bool thisIntersects = false, fwd;
|
||||
FindEdgeOn(a, b, &n, cnt, coplanarIsInter, &thisIntersects, &fwd);
|
||||
if(n != 1) {
|
||||
sel->AddEdge(a, b);
|
||||
if(leaky) *leaky = true;
|
||||
}
|
||||
if(thisIntersects) {
|
||||
sel->AddEdge(a, b);
|
||||
if(inter) *inter = true;
|
||||
}
|
||||
|
||||
cnt++;
|
||||
}
|
||||
}
|
||||
|
||||
m.Clear();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Report all the edges of the mesh where a front- and back-facing triangle
|
||||
// join. These edges should be drawn when we generate a wireframe drawing
|
||||
// of the part.
|
||||
//-----------------------------------------------------------------------------
|
||||
void SKdNode::MakeTurningEdgesInto(SEdgeList *sel) {
|
||||
SMesh m;
|
||||
ZERO(&m);
|
||||
ClearTags();
|
||||
MakeMeshInto(&m);
|
||||
|
||||
int cnt = 1234;
|
||||
int i, j;
|
||||
for(i = 0; i < m.l.n; i++) {
|
||||
STriangle *tr = &(m.l.elem[i]);
|
||||
if(tr->Normal().z > LENGTH_EPS) continue;
|
||||
// So this is a back-facing triangle
|
||||
|
||||
for(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);
|
||||
|
||||
int n = 0;
|
||||
bool inter, fwd;
|
||||
FindEdgeOn(a, b, &n, cnt, true, &inter, &fwd);
|
||||
if(n == 1) {
|
||||
// and its neighbour is front-facing, so generate the edge.
|
||||
if(fwd) sel->AddEdge(a, b);
|
||||
bool thisIntersects = false, fwd;
|
||||
DWORD face;
|
||||
FindEdgeOn(a, b, &n, cnt, coplanarIsInter,
|
||||
&thisIntersects, &fwd, &face);
|
||||
|
||||
switch(how) {
|
||||
case NAKED_OR_SELF_INTER_EDGES:
|
||||
if(n != 1) {
|
||||
sel->AddEdge(a, b);
|
||||
if(leaky) *leaky = true;
|
||||
}
|
||||
if(thisIntersects) {
|
||||
sel->AddEdge(a, b);
|
||||
if(inter) *inter = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case TURNING_EDGES:
|
||||
if((tr->Normal().z < LENGTH_EPS) &&
|
||||
(n == 1) &&
|
||||
fwd)
|
||||
{
|
||||
// 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);
|
||||
}
|
||||
break;
|
||||
|
||||
case EMPHASIZED_EDGES:
|
||||
if(tr->meta.face != face && n == 1) {
|
||||
// The two triangles that join at this edge come from
|
||||
// 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);
|
||||
}
|
||||
break;
|
||||
|
||||
default: oops();
|
||||
}
|
||||
|
||||
cnt++;
|
||||
|
|
10
polygon.h
10
polygon.h
|
@ -192,6 +192,7 @@ public:
|
|||
void MakeFromAssemblyOf(SMesh *a, SMesh *b);
|
||||
|
||||
void MakeEdgesInPlaneInto(SEdgeList *sel, Vector n, double d);
|
||||
void MakeEmphasizedEdgesInto(SEdgeList *sel);
|
||||
|
||||
bool IsEmpty(void);
|
||||
void RemapFaces(Group *g, int remap);
|
||||
|
@ -232,10 +233,13 @@ public:
|
|||
void ClearTags(void);
|
||||
|
||||
void FindEdgeOn(Vector a, Vector b, int *n, int cnt, bool coplanarIsInter,
|
||||
bool *inter, bool *fwd);
|
||||
void MakeNakedEdgesInto(SEdgeList *sel, bool coplanarIsInter,
|
||||
bool *inter, bool *fwd,
|
||||
DWORD *face);
|
||||
static const int NAKED_OR_SELF_INTER_EDGES = 100;
|
||||
static const int TURNING_EDGES = 200;
|
||||
static const int EMPHASIZED_EDGES = 300;
|
||||
void MakeCertainEdgesInto(SEdgeList *sel, int how, bool coplanarIsInter,
|
||||
bool *inter, bool *leaky);
|
||||
void MakeTurningEdgesInto(SEdgeList *sel);
|
||||
|
||||
void OcclusionTestLine(SEdge orig, SEdgeList *sel, int cnt);
|
||||
void SplitLinesAgainstTriangle(SEdgeList *sel, STriangle *tr);
|
||||
|
|
|
@ -213,13 +213,15 @@ void SolveSpace::AfterNewFile(void) {
|
|||
GW.height = h;
|
||||
|
||||
// The triangles haven't been generated yet, but zoom to fit the entities
|
||||
// roughly in the window, since that sets the mesh tolerance.
|
||||
GW.ZoomToFit();
|
||||
// roughly in the window, since that sets the mesh tolerance. Consider
|
||||
// invisible entities, so we still get something reasonable if the only
|
||||
// thing visible is the not-yet-generated surfaces.
|
||||
GW.ZoomToFit(true);
|
||||
|
||||
GenerateAll(0, INT_MAX);
|
||||
later.showTW = true;
|
||||
// Then zoom to fit again, to fit the triangles
|
||||
GW.ZoomToFit();
|
||||
GW.ZoomToFit(false);
|
||||
|
||||
UpdateWindowTitle();
|
||||
}
|
||||
|
@ -420,7 +422,9 @@ void SolveSpace::MenuAnalyze(int id) {
|
|||
SMesh *m = &(SK.GetGroup(SS.GW.activeGroup)->displayMesh);
|
||||
SKdNode *root = SKdNode::From(m);
|
||||
bool inters, leaks;
|
||||
root->MakeNakedEdgesInto(&(SS.nakedEdges), true, &inters, &leaks);
|
||||
root->MakeCertainEdgesInto(&(SS.nakedEdges),
|
||||
SKdNode::NAKED_OR_SELF_INTER_EDGES, true, &inters, &leaks);
|
||||
|
||||
InvalidateGraphics();
|
||||
|
||||
char *intersMsg = inters ?
|
||||
|
@ -446,7 +450,9 @@ void SolveSpace::MenuAnalyze(int id) {
|
|||
SMesh *m = &(SK.GetGroup(SS.GW.activeGroup)->displayMesh);
|
||||
SKdNode *root = SKdNode::From(m);
|
||||
bool inters, leaks;
|
||||
root->MakeNakedEdgesInto(&(SS.nakedEdges), false, &inters, &leaks);
|
||||
root->MakeCertainEdgesInto(&(SS.nakedEdges),
|
||||
SKdNode::NAKED_OR_SELF_INTER_EDGES, false, &inters, &leaks);
|
||||
|
||||
InvalidateGraphics();
|
||||
|
||||
if(inters) {
|
||||
|
|
5
ui.h
5
ui.h
|
@ -301,8 +301,9 @@ public:
|
|||
Vector VectorFromProjs(Vector rightUpForward);
|
||||
void HandlePointForZoomToFit(Vector p, Point2d *pmax, Point2d *pmin,
|
||||
double *wmin, bool div);
|
||||
void LoopOverPoints(Point2d *pmax, Point2d *pmin, double *wmin, bool div);
|
||||
void ZoomToFit(void);
|
||||
void LoopOverPoints(Point2d *pmax, Point2d *pmin, double *wmin, bool div,
|
||||
bool includingInvisibles);
|
||||
void ZoomToFit(bool includingInvisibles);
|
||||
|
||||
hGroup activeGroup;
|
||||
void EnsureValidActives(void);
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
|
||||
marching algorithm for surface intersection
|
||||
tangent intersections
|
||||
put back the meshes
|
||||
|
||||
-----
|
||||
line styles (color, thickness)
|
||||
loop detection
|
||||
incremental regen of entities?
|
||||
IGES and STEP export
|
||||
incremental regen of entities?
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue