FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

Classify Boolean edges where a tangent fillet surface folds onto the shell. Fixes #1291. by BoykoNeov · Pull Request #1731 · solvespace/solvespace · GitHub

Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension .cpp  (13) .h  (1) .slvs  (5) .txt  (1) All 4 file types selected
Viewed files
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Unified
Split
Hide whitespace
Diff view
Unified
Split
Hide whitespace
3 changes: 1 addition & 2 deletions src/export.cpp
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -407,8 +407,7 @@ void SolveSpaceUI::ExportLinesAndMesh(SEdgeList *sel, SBezierList *sbl, SMesh *s
// Generate the edges where a curved surface turns from front-facing
// to back-facing.
if(SS.GW.showEdges || SS.GW.showOutlines) {
root->MakeCertainEdgesInto(sel, EdgeKind::TURNING,
/*coplanarIsInter=*/false, NULL, NULL,
root->MakeCertainEdgesInto(sel, EdgeKind::TURNING, NULL, NULL,
GW.showOutlines ? Style::OUTLINE : Style::SOLID_EDGE);
}

Expand Down
63 changes: 29 additions & 34 deletions src/mesh.cpp
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,7 @@ 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->MakeCertainEdgesInto(sel, EdgeKind::NAKED_OR_SELF_INTER,
/*coplanarIsInter=*/false, NULL, NULL);
root->MakeCertainEdgesInto(sel, EdgeKind::NAKED_OR_SELF_INTER, NULL, NULL);

m.Clear();
}
Expand Down Expand Up @@ -830,25 +829,22 @@ void SKdNode::OcclusionTestLine(SEdge orig, SEdgeList *sel, int cnt) const {
// for the edge from a to b), and increment info->count each time that we
// find one. If a triangle is found, then report whether it is front- or
// back-facing using info->frontFacing. And regardless of whether a mate is
// found, report whether the edge intersects the mesh with info->intersectsMesh;
// if coplanarIsInter then we count the edge as intersecting if it's coplanar
// with a triangle in the mesh, otherwise not.
// found, report whether the edge intersects the mesh with info->intersectsMesh.
//-----------------------------------------------------------------------------
void SKdNode::FindEdgeOn(Vector a, Vector b, int cnt, bool coplanarIsInter,
EdgeOnInfo *info) const
void SKdNode::FindEdgeOn(Vector a, Vector b, int cnt, EdgeOnInfo *info) const
{
if(gt && lt) {
double ac = a.Element(which),
bc = b.Element(which);
if(ac < c + KDTREE_EPS ||
bc < c + KDTREE_EPS)
{
lt->FindEdgeOn(a, b, cnt, coplanarIsInter, info);
lt->FindEdgeOn(a, b, cnt, info);
}
if(ac > c - KDTREE_EPS ||
bc > c - KDTREE_EPS)
{
gt->FindEdgeOn(a, b, cnt, coplanarIsInter, info);
gt->FindEdgeOn(a, b, cnt, info);
}
return;
}
Expand Down Expand Up @@ -898,28 +894,27 @@ void SKdNode::FindEdgeOn(Vector a, Vector b, int cnt, bool coplanarIsInter,
// The edge crosses the plane of the triangle; now see if
// it crosses inside the triangle.
if(tr->ContainsPointProjd(b.Minus(a), a)) {
if(coplanarIsInter) {
info->intersectsMesh = true;
Vector p = Vector::AtIntersectionOfPlaneAndLine(
n, d, a, b, NULL);
Vector ta = tr->a,
tb = tr->b,
tc = tr->c;
if((p.DistanceToLine(ta, tb.Minus(ta)) < LENGTH_EPS) ||
(p.DistanceToLine(tb, tc.Minus(tb)) < LENGTH_EPS) ||
(p.DistanceToLine(tc, ta.Minus(tc)) < LENGTH_EPS))
{
// Intersection lies on edge. This happens when
// our edge is from a triangle coplanar with, or
// tangent to, another triangle in the mesh; for
// example where a surface of a Boolean result
// grazes the interior of a face without cutting
// it. That is a touch, not a crossing: an edge
// that actually passes into the mesh here also
// passes through the interior of the triangles
// on the other side of that edge, and is
// reported there.
} else {
Vector p = Vector::AtIntersectionOfPlaneAndLine(
n, d, a, b, NULL);
Vector ta = tr->a,
tb = tr->b,
tc = tr->c;
if((p.DistanceToLine(ta, tb.Minus(ta)) < LENGTH_EPS) ||
(p.DistanceToLine(tb, tc.Minus(tb)) < LENGTH_EPS) ||
(p.DistanceToLine(tc, ta.Minus(tc)) < LENGTH_EPS))
{
// Intersection lies on edge. This happens when
// our edge is from a triangle coplanar with
// another triangle in the mesh. We don't test
// the edge against triangles whose plane contains
// that edge, but we do end up testing against
// the coplanar triangle's neighbours, which we
// will intersect on their edges.
} else {
info->intersectsMesh = true;
}
info->intersectsMesh = true;
}
}
}
Expand Down Expand Up @@ -951,7 +946,7 @@ 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, EdgeKind how, bool coplanarIsInter,
void SKdNode::MakeCertainEdgesInto(SEdgeList *sel, EdgeKind how,
bool *inter, bool *leaky, int auxA) const
{
if(inter) *inter = false;
Expand All @@ -969,15 +964,15 @@ void SKdNode::MakeCertainEdgesInto(SEdgeList *sel, EdgeKind how, bool coplanarIs
Vector b = tr->vertices[(j + 1) % 3];

SKdNode::EdgeOnInfo info = {};
FindEdgeOn(a, b, cnt, coplanarIsInter, &info);
FindEdgeOn(a, b, cnt, &info);

switch(how) {
case EdgeKind::NAKED_OR_SELF_INTER:
// there should be one anti-parllel edge
if(info.count != 1) {
// but there may be multiple parallel coincident edges
SKdNode::EdgeOnInfo parallelInfo = {};
FindEdgeOn(b, a, -cnt, coplanarIsInter, &parallelInfo);
FindEdgeOn(b, a, -cnt, &parallelInfo);
if (info.count != parallelInfo.count) {
sel->AddEdge(a, b, auxA);
if(leaky) *leaky = true;
Expand Down Expand Up @@ -1059,7 +1054,7 @@ void SKdNode::MakeOutlinesInto(SOutlineList *sol, EdgeKind edgeKind) const
Vector b = tr->vertices[(j + 1) % 3];

SKdNode::EdgeOnInfo info = {};
FindEdgeOn(a, b, cnt, /*coplanarIsInter=*/false, &info);
FindEdgeOn(a, b, cnt, &info);
cnt++;
if(info.count != 1) continue;
if(CheckAndAddTrianglePair(&edgeTris, tr, info.tr))
Expand Down
4 changes: 2 additions & 2 deletions src/polygon.h
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -362,8 +362,8 @@ class SKdNode {
void ListTrianglesInto(std::vector<STriangle *> *tl) const;
void ClearTags() const;

void FindEdgeOn(Vector a, Vector b, int cnt, bool coplanarIsInter, EdgeOnInfo *info) const;
void MakeCertainEdgesInto(SEdgeList *sel, EdgeKind how, bool coplanarIsInter,
void FindEdgeOn(Vector a, Vector b, int cnt, EdgeOnInfo *info) const;
void MakeCertainEdgesInto(SEdgeList *sel, EdgeKind how,
bool *inter, bool *leaky, int auxA = 0) const;
void MakeOutlinesInto(SOutlineList *sel, EdgeKind tagKind) const;

Expand Down
4 changes: 2 additions & 2 deletions src/solvespace.cpp
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -905,7 +905,7 @@ void SolveSpaceUI::MenuAnalyze(Command id) {
SKdNode *root = SKdNode::From(m);
bool inters, leaks;
root->MakeCertainEdgesInto(&(SS.nakedEdges),
EdgeKind::SELF_INTER, /*coplanarIsInter=*/false, &inters, &leaks);
EdgeKind::SELF_INTER, &inters, &leaks);

SS.GW.Invalidate();

Expand Down Expand Up @@ -1070,7 +1070,7 @@ void SolveSpaceUI::ShowNakedEdges(bool reportOnlyWhenNotOkay) {
SKdNode *root = SKdNode::From(m);
bool inters, leaks;
root->MakeCertainEdgesInto(&(SS.nakedEdges),
EdgeKind::NAKED_OR_SELF_INTER, /*coplanarIsInter=*/true, &inters, &leaks);
EdgeKind::NAKED_OR_SELF_INTER, &inters, &leaks);

if(reportOnlyWhenNotOkay && !inters && !leaks && SS.nakedEdges.l.IsEmpty()) {
return;
Expand Down
54 changes: 43 additions & 11 deletions src/srf/boolean.cpp
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -452,9 +452,15 @@ void SSurface::EdgeNormalsWithinSurface(Point2d auv, Point2d buv,

*surfn = NormalAt(muv.x, muv.y);

// Compute the edge's inner normal in xyz space.
// Compute the edge's inner normal in xyz space. Use the configured
// chord tolerance as the probe distance, but never more than a
// fraction of this edge's own length: the classification must probe
// the surfaces close to the edge, and a coarse tolerance on a small
// model would otherwise evaluate them far away, or even extrapolate
// them outside their domain, and misclassify.
Vector ab = (PointAt(auv)).Minus(PointAt(buv)),
enxyz = (ab.Cross(*surfn)).WithMagnitude(SS.ChordTolMm());
enxyz = (ab.Cross(*surfn)).WithMagnitude(
min(SS.ChordTolMm(), ab.Magnitude() / 10));
// And based on that, compute the edge's inner normal in uv space. This
// vector is perpendicular to the edge in xyz, but not necessarily in uv.
Vector tu, tv, tx, ty;
Expand Down Expand Up @@ -559,8 +565,20 @@ SSurface SSurface::MakeCopyTrimAgainst(SShell *parent,
// We are subtracting the portion of our surface that
// lies in the shell, so the in-plane edge normal should
// point opposite to the surface normal.
Vector tnxd = tn.Cross(b.Minus(a));
double dot = tnxd.Dot(sn);
if(fabs(dot) < SShell::DOTP_TOL*tnxd.Magnitude()*sn.Magnitude()) {
// The surfaces are tangent along this curve, so their
// normals give no orientation for it. Add the edge in
// both directions; the classification against the
// shells keeps the correctly oriented one and
// discards the other.
inter.AddEdge(ta, tb, sc.h.v, 0);
inter.AddEdge(tb, ta, sc.h.v, 1);
continue;
}
bool bkwds = true;
if((tn.Cross(b.Minus(a))).Dot(sn) < 0) bkwds = !bkwds;
if(dot < 0) bkwds = !bkwds;
if((type == SSurface::CombineAs::DIFFERENCE && !opA) ||
(type == SSurface::CombineAs::INTERSECTION)) { // Invert all newly created edges for intersection
bkwds = !bkwds;
Expand Down Expand Up @@ -617,14 +635,21 @@ SSurface SSurface::MakeCopyTrimAgainst(SShell *parent,
ret.EdgeNormalsWithinSurface(auv, buv, &pt, &enin, &enout, &surfn,
se->auxA, into, sha, shb);

SShell::Class indir_shell, outdir_shell, indir_orig, outdir_orig;
// Initialize to a deterministic (if arbitrary) guess, so that if the
// classification fails we don't use values from uninitialized stack.
SShell::Class indir_shell = SShell::Class::SURF_OUTSIDE,
outdir_shell = SShell::Class::SURF_OUTSIDE,
indir_orig, outdir_orig;

indir_orig = SShell::Class::SURF_INSIDE;
outdir_orig = SShell::Class::SURF_OUTSIDE;

agnst->ClassifyEdge(&indir_shell, &outdir_shell,
ret.PointAt(auv), ret.PointAt(buv), pt,
enin, enout, surfn);
if(!agnst->ClassifyEdge(&indir_shell, &outdir_shell,
ret.PointAt(auv), ret.PointAt(buv), pt,
enin, enout, surfn))
{
dbp("MakeCopyTrimAgainst: failed to classify orig edge (I=%d)", I+dbg_index);
}

if(KeepEdge(type, opA, indir_shell, outdir_shell,
indir_orig, outdir_orig))
Expand All @@ -650,14 +675,21 @@ SSurface SSurface::MakeCopyTrimAgainst(SShell *parent,
ret.EdgeNormalsWithinSurface(auv, buv, &pt, &enin, &enout, &surfn,
se->auxA, into, sha, shb);

SShell::Class indir_shell, outdir_shell, indir_orig, outdir_orig;
// Initialize to a deterministic (if arbitrary) guess, so that if the
// classification fails we don't use values from uninitialized stack.
SShell::Class indir_shell = SShell::Class::SURF_OUTSIDE,
outdir_shell = SShell::Class::SURF_OUTSIDE,
indir_orig, outdir_orig;

SBspUv::Class c_this = (origBsp) ? origBsp->ClassifyEdge(auv, buv, &ret) : SBspUv::Class::OUTSIDE;
TagByClassifiedEdge(c_this, &indir_orig, &outdir_orig);

agnst->ClassifyEdge(&indir_shell, &outdir_shell,
ret.PointAt(auv), ret.PointAt(buv), pt,
enin, enout, surfn);
if(!agnst->ClassifyEdge(&indir_shell, &outdir_shell,
ret.PointAt(auv), ret.PointAt(buv), pt,
enin, enout, surfn))
{
dbp("MakeCopyTrimAgainst: failed to classify inter edge (I=%d)", I+dbg_index);
}

if(KeepEdge(type, opA, indir_shell, outdir_shell,
indir_orig, outdir_orig))
Expand Down
Loading
Loading

Back | FazBrowse Home | New Git URL