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
Jonathan Westhues 2009-05-28 21:40:17 -08:00
parent 7536ccb054
commit 842645d61f
8 changed files with 122 additions and 81 deletions

View File

@ -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) {

View File

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

View File

@ -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
View File

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

View File

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

View File

@ -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
View File

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

View File

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