|
@@ -50,42 +50,97 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
#include "ProcessHelper.h"
|
|
|
|
|
|
#include <iterator>
|
|
|
+#include <boost/tuple/tuple.hpp>
|
|
|
+
|
|
|
|
|
|
namespace Assimp {
|
|
|
namespace IFC {
|
|
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
|
-enum Intersect {
|
|
|
- Intersect_No,
|
|
|
- Intersect_LiesOnPlane,
|
|
|
- Intersect_Yes
|
|
|
-};
|
|
|
-
|
|
|
-// ------------------------------------------------------------------------------------------------
|
|
|
-Intersect IntersectSegmentPlane(const IfcVector3& p,const IfcVector3& n, const IfcVector3& e0,
|
|
|
- const IfcVector3& e1,
|
|
|
- IfcVector3& out)
|
|
|
+// Calculates intersection between line segment and plane. To catch corner cases, specify which side you prefer.
|
|
|
+// The function then generates a hit only if the end is beyond a certain margin in that direction, filtering out
|
|
|
+// "very close to plane" ghost hits as long as start and end stay directly on or within the given plane side.
|
|
|
+bool IntersectSegmentPlane(const IfcVector3& p,const IfcVector3& n, const IfcVector3& e0,
|
|
|
+ const IfcVector3& e1, bool assumeStartOnWhiteSide, IfcVector3& out)
|
|
|
{
|
|
|
- const IfcVector3 pdelta = e0 - p, seg = e1-e0;
|
|
|
+ const IfcVector3 pdelta = e0 - p, seg = e1 - e0;
|
|
|
const IfcFloat dotOne = n*seg, dotTwo = -(n*pdelta);
|
|
|
|
|
|
- if (std::fabs(dotOne) < 1e-6) {
|
|
|
- return std::fabs(dotTwo) < 1e-6f ? Intersect_LiesOnPlane : Intersect_No;
|
|
|
+ // if segment ends on plane, do not report a hit. We stay on that side until a following segment starting at this
|
|
|
+ // point leaves the plane through the other side
|
|
|
+ if( std::abs(dotOne + dotTwo) < 1e-6 )
|
|
|
+ return false;
|
|
|
+
|
|
|
+ // if segment starts on the plane, report a hit only if the end lies on the *other* side
|
|
|
+ if( std::abs(dotTwo) < 1e-6 )
|
|
|
+ {
|
|
|
+ if( (assumeStartOnWhiteSide && dotOne + dotTwo < 1e-6) || (!assumeStartOnWhiteSide && dotOne + dotTwo > -1e-6) )
|
|
|
+ {
|
|
|
+ out = e0;
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- const IfcFloat t = dotTwo/dotOne;
|
|
|
+ // ignore if segment is parallel to plane and far away from it on either side
|
|
|
+ // Warning: if there's a few thousand of such segments which slowly accumulate beyond the epsilon, no hit would be registered
|
|
|
+ if( std::abs(dotOne) < 1e-6 )
|
|
|
+ return false;
|
|
|
+
|
|
|
// t must be in [0..1] if the intersection point is within the given segment
|
|
|
- if (t > 1.f || t < 0.f) {
|
|
|
- return Intersect_No;
|
|
|
+ const IfcFloat t = dotTwo / dotOne;
|
|
|
+ if( t > 1.0 || t < 0.0 )
|
|
|
+ return false;
|
|
|
+
|
|
|
+ out = e0 + t*seg;
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+// ------------------------------------------------------------------------------------------------
|
|
|
+void FilterPolygon(std::vector<IfcVector3>& resultpoly)
|
|
|
+{
|
|
|
+ if( resultpoly.size() < 3 )
|
|
|
+ {
|
|
|
+ resultpoly.clear();
|
|
|
+ return;
|
|
|
}
|
|
|
- out = e0+t*seg;
|
|
|
- return Intersect_Yes;
|
|
|
+
|
|
|
+ IfcVector3 vmin, vmax;
|
|
|
+ ArrayBounds(resultpoly.data(), resultpoly.size(), vmin, vmax);
|
|
|
+
|
|
|
+ // filter our IfcFloat points - those may happen if a point lies
|
|
|
+ // directly on the intersection line or directly on the clipping plane
|
|
|
+ const IfcFloat epsilon = (vmax - vmin).SquareLength() / 1e6f;
|
|
|
+ FuzzyVectorCompare fz(epsilon);
|
|
|
+ std::vector<IfcVector3>::iterator e = std::unique(resultpoly.begin(), resultpoly.end(), fz);
|
|
|
+
|
|
|
+ if( e != resultpoly.end() )
|
|
|
+ resultpoly.erase(e, resultpoly.end());
|
|
|
+
|
|
|
+ if( !resultpoly.empty() && fz(resultpoly.front(), resultpoly.back()) )
|
|
|
+ resultpoly.pop_back();
|
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
|
-void ProcessBooleanHalfSpaceDifference(const IfcHalfSpaceSolid* hs, TempMesh& result,
|
|
|
- const TempMesh& first_operand,
|
|
|
- ConversionData& /*conv*/)
|
|
|
+void WritePolygon(std::vector<IfcVector3>& resultpoly, TempMesh& result)
|
|
|
+{
|
|
|
+ FilterPolygon(resultpoly);
|
|
|
+
|
|
|
+ if( resultpoly.size() > 2 )
|
|
|
+ {
|
|
|
+ result.verts.insert(result.verts.end(), resultpoly.begin(), resultpoly.end());
|
|
|
+ result.vertcnt.push_back(resultpoly.size());
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+// ------------------------------------------------------------------------------------------------
|
|
|
+void ProcessBooleanHalfSpaceDifference(const IfcHalfSpaceSolid* hs, TempMesh& result,
|
|
|
+ const TempMesh& first_operand,
|
|
|
+ ConversionData& /*conv*/)
|
|
|
{
|
|
|
ai_assert(hs != NULL);
|
|
|
|
|
@@ -120,20 +175,14 @@ void ProcessBooleanHalfSpaceDifference(const IfcHalfSpaceSolid* hs, TempMesh& re
|
|
|
for(iit = begin; iit != end; vidx += *iit++) {
|
|
|
|
|
|
unsigned int newcount = 0;
|
|
|
- for(unsigned int i = 0; i < *iit; ++i) {
|
|
|
- const IfcVector3& e0 = in[vidx+i], e1 = in[vidx+(i+1)%*iit];
|
|
|
+ bool isAtWhiteSide = (in[vidx] - p) * n > -1e-6;
|
|
|
+ for( unsigned int i = 0; i < *iit; ++i ) {
|
|
|
+ const IfcVector3& e0 = in[vidx + i], e1 = in[vidx + (i + 1) % *iit];
|
|
|
|
|
|
// does the next segment intersect the plane?
|
|
|
IfcVector3 isectpos;
|
|
|
- const Intersect isect = IntersectSegmentPlane(p,n,e0,e1,isectpos);
|
|
|
- if (isect == Intersect_No || isect == Intersect_LiesOnPlane) {
|
|
|
- if ( (e0-p).Normalize()*n > 0 ) {
|
|
|
- outvert.push_back(e0);
|
|
|
- ++newcount;
|
|
|
- }
|
|
|
- }
|
|
|
- else if (isect == Intersect_Yes) {
|
|
|
- if ( (e0-p).Normalize()*n > 0 ) {
|
|
|
+ if( IntersectSegmentPlane(p, n, e0, e1, isAtWhiteSide, isectpos) ) {
|
|
|
+ if( isAtWhiteSide ) {
|
|
|
// e0 is on the right side, so keep it
|
|
|
outvert.push_back(e0);
|
|
|
outvert.push_back(isectpos);
|
|
@@ -144,8 +193,16 @@ void ProcessBooleanHalfSpaceDifference(const IfcHalfSpaceSolid* hs, TempMesh& re
|
|
|
outvert.push_back(isectpos);
|
|
|
++newcount;
|
|
|
}
|
|
|
+ isAtWhiteSide = !isAtWhiteSide;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ if( isAtWhiteSide ) {
|
|
|
+ outvert.push_back(e0);
|
|
|
+ ++newcount;
|
|
|
+ }
|
|
|
}
|
|
|
- }
|
|
|
+ }
|
|
|
|
|
|
if (!newcount) {
|
|
|
continue;
|
|
@@ -185,76 +242,114 @@ void ProcessBooleanHalfSpaceDifference(const IfcHalfSpaceSolid* hs, TempMesh& re
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
|
// Check if e0-e1 intersects a sub-segment of the given boundary line.
|
|
|
// note: this functions works on 3D vectors, but performs its intersection checks solely in xy.
|
|
|
-bool IntersectsBoundaryProfile( const IfcVector3& e0, const IfcVector3& e1, const std::vector<IfcVector3>& boundary,
|
|
|
- std::vector<size_t>& intersected_boundary_segments,
|
|
|
- std::vector<IfcVector3>& intersected_boundary_points,
|
|
|
- bool half_open = false,
|
|
|
- bool* e0_hits_border = NULL)
|
|
|
+// New version takes the supposed inside/outside state as a parameter and treats corner cases as if
|
|
|
+// the line stays on that side. This should make corner cases more stable.
|
|
|
+// Two million assumptions! Boundary should have all z at 0.0, will be treated as closed, should not have
|
|
|
+// segments with length <1e-6, self-intersecting might break the corner case handling... just don't go there, ok?
|
|
|
+bool IntersectsBoundaryProfile(const IfcVector3& e0, const IfcVector3& e1, const std::vector<IfcVector3>& boundary,
|
|
|
+ const bool isStartAssumedInside, std::vector<std::pair<size_t, IfcVector3> >& intersect_results,
|
|
|
+ const bool halfOpen = false)
|
|
|
{
|
|
|
- ai_assert(intersected_boundary_segments.empty());
|
|
|
- ai_assert(intersected_boundary_points.empty());
|
|
|
-
|
|
|
- if(e0_hits_border) {
|
|
|
- *e0_hits_border = false;
|
|
|
+ ai_assert(intersect_results.empty());
|
|
|
+
|
|
|
+ // determine winding order - necessary to detect segments going "inwards" or "outwards" from a point directly on the border
|
|
|
+ // positive sum of angles means clockwise order when looking down the -Z axis
|
|
|
+ IfcFloat windingOrder = 0.0;
|
|
|
+ for( size_t i = 0, bcount = boundary.size(); i < bcount; ++i ) {
|
|
|
+ IfcVector3 b01 = boundary[(i + 1) % bcount] - boundary[i];
|
|
|
+ IfcVector3 b12 = boundary[(i + 2) % bcount] - boundary[(i + 1) % bcount];
|
|
|
+ IfcVector3 b1_side = IfcVector3(b01.y, -b01.x, 0.0); // rotated 90° clockwise in Z plane
|
|
|
+ // Warning: rough estimate only. A concave poly with lots of small segments each featuring a small counter rotation
|
|
|
+ // could fool the accumulation. Correct implementation would be sum( acos( b01 * b2) * sign( b12 * b1_side))
|
|
|
+ windingOrder += (b1_side.x*b12.x + b1_side.y*b12.y);
|
|
|
}
|
|
|
+ windingOrder = windingOrder > 0.0 ? 1.0 : -1.0;
|
|
|
|
|
|
- const IfcVector3& e = e1 - e0;
|
|
|
+ const IfcVector3 e = e1 - e0;
|
|
|
|
|
|
- for (size_t i = 0, bcount = boundary.size(); i < bcount; ++i) {
|
|
|
+ for( size_t i = 0, bcount = boundary.size(); i < bcount; ++i ) {
|
|
|
// boundary segment i: b0-b1
|
|
|
const IfcVector3& b0 = boundary[i];
|
|
|
- const IfcVector3& b1 = boundary[(i+1) % bcount];
|
|
|
-
|
|
|
- const IfcVector3& b = b1 - b0;
|
|
|
+ const IfcVector3& b1 = boundary[(i + 1) % bcount];
|
|
|
+ IfcVector3 b = b1 - b0;
|
|
|
+ IfcFloat b_sqlen_inv = 1.0 / b.SquareLength();
|
|
|
|
|
|
// segment-segment intersection
|
|
|
// solve b0 + b*s = e0 + e*t for (s,t)
|
|
|
const IfcFloat det = (-b.x * e.y + e.x * b.y);
|
|
|
- if(std::fabs(det) < 1e-6) {
|
|
|
+ if( std::abs(det) < 1e-6 ) {
|
|
|
// no solutions (parallel lines)
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
const IfcFloat x = b0.x - e0.x;
|
|
|
const IfcFloat y = b0.y - e0.y;
|
|
|
-
|
|
|
- const IfcFloat s = (x*e.y - e.x*y)/det;
|
|
|
- const IfcFloat t = (x*b.y - b.x*y)/det;
|
|
|
-
|
|
|
+ const IfcFloat s = (x*e.y - e.x*y) / det; // scale along boundary edge
|
|
|
+ const IfcFloat t = (x*b.y - b.x*y) / det; // scale along given segment
|
|
|
+ const IfcVector3 p = e0 + e*t;
|
|
|
#ifdef ASSIMP_BUILD_DEBUG
|
|
|
- const IfcVector3 check = b0 + b*s - (e0 + e*t);
|
|
|
- ai_assert((IfcVector2(check.x,check.y)).SquareLength() < 1e-5);
|
|
|
+ const IfcVector3 check = b0 + b*s - p;
|
|
|
+ ai_assert((IfcVector2(check.x, check.y)).SquareLength() < 1e-5);
|
|
|
#endif
|
|
|
|
|
|
- // for a valid intersection, s-t should be in range [0,1].
|
|
|
- // note that for t (i.e. the segment point) we only use a
|
|
|
- // half-sided epsilon because the next segment should catch
|
|
|
- // this case.
|
|
|
- const IfcFloat epsilon = 1e-6;
|
|
|
- if (t >= -epsilon && (t <= 1.0+epsilon || half_open) && s >= -epsilon && s <= 1.0) {
|
|
|
+ // also calculate the distance of e0 and e1 to the segment. We need to detect the "starts directly on segment"
|
|
|
+ // and "ends directly at segment" cases
|
|
|
+ bool startsAtSegment, endsAtSegment;
|
|
|
+ {
|
|
|
+ // calculate closest point to each end on the segment, clamp that point to the segment's length, then check
|
|
|
+ // distance to that point. This approach is like testing if e0 is inside a capped cylinder.
|
|
|
+ IfcFloat et0 = (b.x*(e0.x - b0.x) + b.y*(e0.y - b0.y)) * b_sqlen_inv;
|
|
|
+ IfcVector3 closestPosToE0OnBoundary = b0 + std::max(IfcFloat(0.0), std::min(IfcFloat(1.0), et0)) * b;
|
|
|
+ startsAtSegment = (closestPosToE0OnBoundary - IfcVector3(e0.x, e0.y, 0.0)).SquareLength() < 1e-12;
|
|
|
+ IfcFloat et1 = (b.x*(e1.x - b0.x) + b.y*(e1.y - b0.y)) * b_sqlen_inv;
|
|
|
+ IfcVector3 closestPosToE1OnBoundary = b0 + std::max(IfcFloat(0.0), std::min(IfcFloat(1.0), et1)) * b;
|
|
|
+ endsAtSegment = (closestPosToE1OnBoundary - IfcVector3(e1.x, e1.y, 0.0)).SquareLength() < 1e-12;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Line segment ends at boundary -> ignore any hit, it will be handled by possibly following segments
|
|
|
+ if( endsAtSegment && !halfOpen )
|
|
|
+ continue;
|
|
|
|
|
|
- if (e0_hits_border && !*e0_hits_border) {
|
|
|
- *e0_hits_border = std::fabs(t) < 1e-5f;
|
|
|
+ // Line segment starts at boundary -> generate a hit only if following that line would change the INSIDE/OUTSIDE
|
|
|
+ // state. This should catch the case where a connected set of segments has a point directly on the boundary,
|
|
|
+ // one segment not hitting it because it ends there and the next segment not hitting it because it starts there
|
|
|
+ // Should NOT generate a hit if the segment only touches the boundary but turns around and stays inside.
|
|
|
+ if( startsAtSegment )
|
|
|
+ {
|
|
|
+ IfcVector3 inside_dir = IfcVector3(b.y, -b.x, 0.0) * windingOrder;
|
|
|
+ bool isGoingInside = (inside_dir * e) > 0.0;
|
|
|
+ if( isGoingInside == isStartAssumedInside )
|
|
|
+ continue;
|
|
|
+
|
|
|
+ // only insert the point into the list if it is sufficiently far away from the previous intersection point.
|
|
|
+ // This way, we avoid duplicate detection if the intersection is directly on the vertex between two segments.
|
|
|
+ if( !intersect_results.empty() && intersect_results.back().first == i - 1 )
|
|
|
+ {
|
|
|
+ const IfcVector3 diff = intersect_results.back().second - e0;
|
|
|
+ if( IfcVector2(diff.x, diff.y).SquareLength() < 1e-10 )
|
|
|
+ continue;
|
|
|
}
|
|
|
-
|
|
|
- const IfcVector3& p = e0 + e*t;
|
|
|
-
|
|
|
- // only insert the point into the list if it is sufficiently
|
|
|
- // far away from the previous intersection point. This way,
|
|
|
- // we avoid duplicate detection if the intersection is
|
|
|
- // directly on the vertex between two segments.
|
|
|
- if (!intersected_boundary_points.empty() && intersected_boundary_segments.back()==i-1 ) {
|
|
|
- const IfcVector3 diff = intersected_boundary_points.back() - p;
|
|
|
- if(IfcVector2(diff.x, diff.y).SquareLength() < 1e-7) {
|
|
|
+ intersect_results.push_back(std::make_pair(i, e0));
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ // for a valid intersection, s and t should be in range [0,1]. Including a bit of epsilon on s, potential double
|
|
|
+ // hits on two consecutive boundary segments are filtered
|
|
|
+ if( s >= -1e-6 * b_sqlen_inv && s <= 1.0 + 1e-6*b_sqlen_inv && t >= 0.0 && (t <= 1.0 || halfOpen) )
|
|
|
+ {
|
|
|
+ // only insert the point into the list if it is sufficiently far away from the previous intersection point.
|
|
|
+ // This way, we avoid duplicate detection if the intersection is directly on the vertex between two segments.
|
|
|
+ if( !intersect_results.empty() && intersect_results.back().first == i - 1 )
|
|
|
+ {
|
|
|
+ const IfcVector3 diff = intersect_results.back().second - p;
|
|
|
+ if( IfcVector2(diff.x, diff.y).SquareLength() < 1e-10 )
|
|
|
continue;
|
|
|
- }
|
|
|
}
|
|
|
- intersected_boundary_segments.push_back(i);
|
|
|
- intersected_boundary_points.push_back(p);
|
|
|
+ intersect_results.push_back(std::make_pair(i, p));
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- return !intersected_boundary_segments.empty();
|
|
|
+ return !intersect_results.empty();
|
|
|
}
|
|
|
|
|
|
|
|
@@ -272,47 +367,21 @@ bool PointInPoly(const IfcVector3& p, const std::vector<IfcVector3>& boundary)
|
|
|
// the border of the polygon. If any of our attempts produces this result,
|
|
|
// we return false immediately.
|
|
|
|
|
|
- std::vector<size_t> intersected_boundary_segments;
|
|
|
- std::vector<IfcVector3> intersected_boundary_points;
|
|
|
+ std::vector<std::pair<size_t, IfcVector3> > intersected_boundary;
|
|
|
size_t votes = 0;
|
|
|
|
|
|
- bool is_border;
|
|
|
- IntersectsBoundaryProfile(p, p + IfcVector3(1.0,0,0), boundary,
|
|
|
- intersected_boundary_segments,
|
|
|
- intersected_boundary_points, true, &is_border);
|
|
|
-
|
|
|
- if(is_border) {
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- votes += intersected_boundary_segments.size() % 2;
|
|
|
-
|
|
|
- intersected_boundary_segments.clear();
|
|
|
- intersected_boundary_points.clear();
|
|
|
-
|
|
|
- IntersectsBoundaryProfile(p, p + IfcVector3(0,1.0,0), boundary,
|
|
|
- intersected_boundary_segments,
|
|
|
- intersected_boundary_points, true, &is_border);
|
|
|
-
|
|
|
- if(is_border) {
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- votes += intersected_boundary_segments.size() % 2;
|
|
|
+ IntersectsBoundaryProfile(p, p + IfcVector3(1.0, 0, 0), boundary, true, intersected_boundary, true);
|
|
|
+ votes += intersected_boundary.size() % 2;
|
|
|
|
|
|
- intersected_boundary_segments.clear();
|
|
|
- intersected_boundary_points.clear();
|
|
|
+ intersected_boundary.clear();
|
|
|
+ IntersectsBoundaryProfile(p, p + IfcVector3(0, 1.0, 0), boundary, true, intersected_boundary, true);
|
|
|
+ votes += intersected_boundary.size() % 2;
|
|
|
|
|
|
- IntersectsBoundaryProfile(p, p + IfcVector3(0.6,-0.6,0.0), boundary,
|
|
|
- intersected_boundary_segments,
|
|
|
- intersected_boundary_points, true, &is_border);
|
|
|
+ intersected_boundary.clear();
|
|
|
+ IntersectsBoundaryProfile(p, p + IfcVector3(0.6, -0.6, 0.0), boundary, true, intersected_boundary, true);
|
|
|
+ votes += intersected_boundary.size() % 2;
|
|
|
|
|
|
- if(is_border) {
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- votes += intersected_boundary_segments.size() % 2;
|
|
|
- //ai_assert(votes == 3 || votes == 0);
|
|
|
+// ai_assert(votes == 3 || votes == 0);
|
|
|
return votes > 1;
|
|
|
}
|
|
|
|
|
@@ -350,6 +419,9 @@ void ProcessPolygonalBoundedBooleanHalfSpaceDifference(const IfcPolygonalBounded
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
+ // determine winding order by calculating the normal.
|
|
|
+ IfcVector3 profileNormal = TempMesh::ComputePolygonNormal(profile->verts.data(), profile->verts.size());
|
|
|
+
|
|
|
IfcMatrix4 proj_inv;
|
|
|
ConvertAxisPlacement(proj_inv,hs->Position);
|
|
|
|
|
@@ -361,256 +433,287 @@ void ProcessPolygonalBoundedBooleanHalfSpaceDifference(const IfcPolygonalBounded
|
|
|
// clip the current contents of `meshout` against the plane we obtained from the second operand
|
|
|
const std::vector<IfcVector3>& in = first_operand.verts;
|
|
|
std::vector<IfcVector3>& outvert = result.verts;
|
|
|
-
|
|
|
- std::vector<unsigned int>::const_iterator begin = first_operand.vertcnt.begin(),
|
|
|
- end = first_operand.vertcnt.end(), iit;
|
|
|
+ std::vector<unsigned int>& outvertcnt = result.vertcnt;
|
|
|
|
|
|
outvert.reserve(in.size());
|
|
|
- result.vertcnt.reserve(first_operand.vertcnt.size());
|
|
|
-
|
|
|
- std::vector<size_t> intersected_boundary_segments;
|
|
|
- std::vector<IfcVector3> intersected_boundary_points;
|
|
|
+ outvertcnt.reserve(first_operand.vertcnt.size());
|
|
|
|
|
|
- // TODO: the following algorithm doesn't handle all cases.
|
|
|
unsigned int vidx = 0;
|
|
|
- for(iit = begin; iit != end; vidx += *iit++) {
|
|
|
- if (!*iit) {
|
|
|
- continue;
|
|
|
+ std::vector<unsigned int>::const_iterator begin = first_operand.vertcnt.begin();
|
|
|
+ std::vector<unsigned int>::const_iterator end = first_operand.vertcnt.end();
|
|
|
+ std::vector<unsigned int>::const_iterator iit;
|
|
|
+ for( iit = begin; iit != end; vidx += *iit++ )
|
|
|
+ {
|
|
|
+ // Our new approach: we cut the poly along the plane, then we intersect the part on the black side of the plane
|
|
|
+ // against the bounding polygon. All the white parts, and the black part outside the boundary polygon, are kept.
|
|
|
+ std::vector<IfcVector3> whiteside, blackside;
|
|
|
+
|
|
|
+ {
|
|
|
+ const IfcVector3* srcVertices = &in[vidx];
|
|
|
+ const size_t srcVtxCount = *iit;
|
|
|
+ if( srcVtxCount == 0 )
|
|
|
+ continue;
|
|
|
+
|
|
|
+ IfcVector3 polyNormal = TempMesh::ComputePolygonNormal(srcVertices, srcVtxCount, true);
|
|
|
+
|
|
|
+ // if the poly is parallel to the plane, put it completely on the black or white side
|
|
|
+ if( std::abs(polyNormal * n) > 0.9999 )
|
|
|
+ {
|
|
|
+ bool isOnWhiteSide = (srcVertices[0] - p) * n > -1e-6;
|
|
|
+ std::vector<IfcVector3>& targetSide = isOnWhiteSide ? whiteside : blackside;
|
|
|
+ targetSide.insert(targetSide.end(), srcVertices, srcVertices + srcVtxCount);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ // otherwise start building one polygon for each side. Whenever the current line segment intersects the plane
|
|
|
+ // we put a point there as an end of the current segment. Then we switch to the other side, put a point there, too,
|
|
|
+ // as a beginning of the current segment, and simply continue accumulating vertices.
|
|
|
+ bool isCurrentlyOnWhiteSide = ((srcVertices[0]) - p) * n > -1e-6;
|
|
|
+ for( size_t a = 0; a < srcVtxCount; ++a )
|
|
|
+ {
|
|
|
+ IfcVector3 e0 = srcVertices[a];
|
|
|
+ IfcVector3 e1 = srcVertices[(a + 1) % srcVtxCount];
|
|
|
+ IfcVector3 ei;
|
|
|
+
|
|
|
+ // put starting point to the current mesh
|
|
|
+ std::vector<IfcVector3>& trgt = isCurrentlyOnWhiteSide ? whiteside : blackside;
|
|
|
+ trgt.push_back(srcVertices[a]);
|
|
|
+
|
|
|
+ // if there's an intersection, put an end vertex there, switch to the other side's mesh,
|
|
|
+ // and add a starting vertex there, too
|
|
|
+ bool isPlaneHit = IntersectSegmentPlane(p, n, e0, e1, isCurrentlyOnWhiteSide, ei);
|
|
|
+ if( isPlaneHit )
|
|
|
+ {
|
|
|
+ if( trgt.empty() || (trgt.back() - ei).SquareLength() > 1e-12 )
|
|
|
+ trgt.push_back(ei);
|
|
|
+ isCurrentlyOnWhiteSide = !isCurrentlyOnWhiteSide;
|
|
|
+ std::vector<IfcVector3>& newtrgt = isCurrentlyOnWhiteSide ? whiteside : blackside;
|
|
|
+ newtrgt.push_back(ei);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- unsigned int newcount = 0;
|
|
|
- bool was_outside_boundary = !PointInPoly(proj * in[vidx], profile->verts);
|
|
|
-
|
|
|
- // used any more?
|
|
|
- //size_t last_intersected_boundary_segment;
|
|
|
- IfcVector3 last_intersected_boundary_point;
|
|
|
-
|
|
|
- bool extra_point_flag = false;
|
|
|
- IfcVector3 extra_point;
|
|
|
-
|
|
|
- IfcVector3 enter_volume;
|
|
|
- bool entered_volume_flag = false;
|
|
|
-
|
|
|
- for(unsigned int i = 0; i < *iit; ++i) {
|
|
|
- // current segment: [i,i+1 mod size] or [*extra_point,i] if extra_point_flag is set
|
|
|
- const IfcVector3& e0 = extra_point_flag ? extra_point : in[vidx+i];
|
|
|
- const IfcVector3& e1 = extra_point_flag ? in[vidx+i] : in[vidx+(i+1)%*iit];
|
|
|
-
|
|
|
- // does the current segment intersect the polygonal boundary?
|
|
|
- const IfcVector3& e0_plane = proj * e0;
|
|
|
- const IfcVector3& e1_plane = proj * e1;
|
|
|
-
|
|
|
- intersected_boundary_segments.clear();
|
|
|
- intersected_boundary_points.clear();
|
|
|
-
|
|
|
- const bool is_outside_boundary = !PointInPoly(e1_plane, profile->verts);
|
|
|
- const bool is_boundary_intersection = is_outside_boundary != was_outside_boundary;
|
|
|
-
|
|
|
- IntersectsBoundaryProfile(e0_plane, e1_plane, profile->verts,
|
|
|
- intersected_boundary_segments,
|
|
|
- intersected_boundary_points);
|
|
|
-
|
|
|
- ai_assert(!is_boundary_intersection || !intersected_boundary_segments.empty());
|
|
|
-
|
|
|
- // does the current segment intersect the plane?
|
|
|
- // (no extra check if this is an extra point)
|
|
|
- IfcVector3 isectpos;
|
|
|
- const Intersect isect = extra_point_flag ? Intersect_No : IntersectSegmentPlane(p,n,e0,e1,isectpos);
|
|
|
+ // the part on the white side can be written into the target mesh right away
|
|
|
+ WritePolygon(whiteside, result);
|
|
|
+
|
|
|
+ // The black part is the piece we need to get rid of, but only the part of it within the boundary polygon.
|
|
|
+ // So we now need to construct all the polygons that result from BlackSidePoly minus BoundaryPoly.
|
|
|
+ FilterPolygon(blackside);
|
|
|
+
|
|
|
+ // Complicated, II. We run along the polygon. a) When we're inside the boundary, we run on until we hit an
|
|
|
+ // intersection, which means we're leaving it. We then start a new out poly there. b) When we're outside the
|
|
|
+ // boundary, we start collecting vertices until we hit an intersection, then we run along the boundary until we hit
|
|
|
+ // an intersection, then we switch back to the poly and run on on this one again, and so on until we got a closed
|
|
|
+ // loop. Then we continue with the path we left to catch potential additional polys on the other side of the
|
|
|
+ // boundary as described in a)
|
|
|
+ if( !blackside.empty() )
|
|
|
+ {
|
|
|
+ // poly edge index, intersection point, edge index in boundary poly
|
|
|
+ std::vector<boost::tuple<size_t, IfcVector3, size_t> > intersections;
|
|
|
+ bool startedInside = PointInPoly(proj * blackside.front(), profile->verts);
|
|
|
+ bool isCurrentlyInside = startedInside;
|
|
|
+
|
|
|
+ std::vector<std::pair<size_t, IfcVector3> > intersected_boundary;
|
|
|
+
|
|
|
+ for( size_t a = 0; a < blackside.size(); ++a )
|
|
|
+ {
|
|
|
+ const IfcVector3 e0 = proj * blackside[a];
|
|
|
+ const IfcVector3 e1 = proj * blackside[(a + 1) % blackside.size()];
|
|
|
+
|
|
|
+ intersected_boundary.clear();
|
|
|
+ IntersectsBoundaryProfile(e0, e1, profile->verts, isCurrentlyInside, intersected_boundary);
|
|
|
+ // sort the hits by distance from e0 to get the correct in/out/in sequence. Manually :-( I miss you, C++11.
|
|
|
+ if( intersected_boundary.size() > 1 )
|
|
|
+ {
|
|
|
+ bool keepSorting = true;
|
|
|
+ while( keepSorting )
|
|
|
+ {
|
|
|
+ keepSorting = false;
|
|
|
+ for( size_t b = 0; b < intersected_boundary.size() - 1; ++b )
|
|
|
+ {
|
|
|
+ if( (intersected_boundary[b + 1].second - e0).SquareLength() < (intersected_boundary[b].second - e0).SquareLength() )
|
|
|
+ {
|
|
|
+ keepSorting = true;
|
|
|
+ std::swap(intersected_boundary[b + 1], intersected_boundary[b]);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // now add them to the list of intersections
|
|
|
+ for( size_t b = 0; b < intersected_boundary.size(); ++b )
|
|
|
+ intersections.push_back(boost::make_tuple(a, proj_inv * intersected_boundary[b].second, intersected_boundary[b].first));
|
|
|
|
|
|
-#ifdef ASSIMP_BUILD_DEBUG
|
|
|
- if (isect == Intersect_Yes) {
|
|
|
- const IfcFloat f = std::fabs((isectpos - p)*n);
|
|
|
- ai_assert(f < 1e-5);
|
|
|
+ // and calculate our new inside/outside state
|
|
|
+ if( intersected_boundary.size() & 1 )
|
|
|
+ isCurrentlyInside = !isCurrentlyInside;
|
|
|
}
|
|
|
-#endif
|
|
|
-
|
|
|
- const bool is_white_side = (e0-p)*n >= -1e-6;
|
|
|
|
|
|
- // e0 on good side of plane? (i.e. we should keep all geometry on this side)
|
|
|
- if (is_white_side) {
|
|
|
- // but is there an intersection in e0-e1 and is e1 in the clipping
|
|
|
- // boundary? In this case, generate a line that only goes to the
|
|
|
- // intersection point.
|
|
|
- if (isect == Intersect_Yes && !is_outside_boundary) {
|
|
|
- outvert.push_back(e0);
|
|
|
- ++newcount;
|
|
|
-
|
|
|
- outvert.push_back(isectpos);
|
|
|
- ++newcount;
|
|
|
-
|
|
|
- /*
|
|
|
- // this is, however, only a line that goes to the plane, but not
|
|
|
- // necessarily to the point where the bounding volume on the
|
|
|
- // black side of the plane is hit. So basically, we need another
|
|
|
- // check for [isectpos-e1], which should yield an intersection
|
|
|
- // point.
|
|
|
- extra_point_flag = true;
|
|
|
- extra_point = isectpos;
|
|
|
-
|
|
|
- was_outside_boundary = true;
|
|
|
- continue; */
|
|
|
-
|
|
|
- // [isectpos, enter_volume] potentially needs extra points.
|
|
|
- // For this, we determine the intersection point with the
|
|
|
- // bounding volume and project it onto the plane.
|
|
|
- /*
|
|
|
- const IfcVector3& enter_volume_proj = proj * enter_volume;
|
|
|
- const IfcVector3& enter_isectpos = proj * isectpos;
|
|
|
-
|
|
|
- intersected_boundary_segments.clear();
|
|
|
- intersected_boundary_points.clear();
|
|
|
-
|
|
|
- IntersectsBoundaryProfile(enter_volume_proj, enter_isectpos, profile->verts,
|
|
|
- intersected_boundary_segments,
|
|
|
- intersected_boundary_points);
|
|
|
-
|
|
|
- if(!intersected_boundary_segments.empty()) {
|
|
|
-
|
|
|
- vec = vec + ((p - vec) * n) * n;
|
|
|
- }
|
|
|
- */
|
|
|
+ // we got a list of in-out-combinations of intersections. That should be an even number of intersections, or
|
|
|
+ // we're fucked.
|
|
|
+ if( (intersections.size() & 1) != 0 )
|
|
|
+ {
|
|
|
+ IFCImporter::LogWarn("Odd number of intersections, can't work with that. Omitting half space boundary check.");
|
|
|
+ continue;
|
|
|
+ }
|
|
|
|
|
|
- //entered_volume_flag = true;
|
|
|
+ if( intersections.size() > 1 )
|
|
|
+ {
|
|
|
+ // If we started outside, the first intersection is a out->in intersection. Cycle them so that it
|
|
|
+ // starts with an intersection leaving the boundary
|
|
|
+ if( !startedInside )
|
|
|
+ for( size_t b = 0; b < intersections.size() - 1; ++b )
|
|
|
+ std::swap(intersections[b], intersections[(b + intersections.size() - 1) % intersections.size()]);
|
|
|
+
|
|
|
+ // Filter pairs of out->in->out that lie too close to each other.
|
|
|
+ for( size_t a = 0; intersections.size() > 0 && a < intersections.size() - 1; /**/ )
|
|
|
+ {
|
|
|
+ if( (intersections[a].get<1>() - intersections[(a + 1) % intersections.size()].get<1>()).SquareLength() < 1e-10 )
|
|
|
+ intersections.erase(intersections.begin() + a, intersections.begin() + a + 2);
|
|
|
+ else
|
|
|
+ a++;
|
|
|
}
|
|
|
- else {
|
|
|
- outvert.push_back(e0);
|
|
|
- ++newcount;
|
|
|
+ if( intersections.size() > 1 && (intersections.back().get<1>() - intersections.front().get<1>()).SquareLength() < 1e-10 )
|
|
|
+ {
|
|
|
+ intersections.pop_back(); intersections.erase(intersections.begin());
|
|
|
}
|
|
|
}
|
|
|
- // e0 on bad side of plane, e1 on good (i.e. we should remove geometry on this side,
|
|
|
- // but only if it is within the bounding volume).
|
|
|
- else if (isect == Intersect_Yes) {
|
|
|
- // is e0 within the clipping volume? Insert the intersection point
|
|
|
- // of [e0,e1] and the plane instead of e0.
|
|
|
- if(was_outside_boundary) {
|
|
|
- outvert.push_back(e0);
|
|
|
- }
|
|
|
- else {
|
|
|
- if(entered_volume_flag) {
|
|
|
- const IfcVector3& fix_point = enter_volume + ((p - enter_volume) * n) * n;
|
|
|
- outvert.push_back(fix_point);
|
|
|
- ++newcount;
|
|
|
- }
|
|
|
|
|
|
- outvert.push_back(isectpos);
|
|
|
+
|
|
|
+ // no intersections at all: either completely inside the boundary, so everything gets discarded, or completely outside.
|
|
|
+ // in the latter case we're implementional lost. I'm simply going to ignore this, so a large poly will not get any
|
|
|
+ // holes if the boundary is smaller and does not touch it anywhere.
|
|
|
+ if( intersections.empty() )
|
|
|
+ {
|
|
|
+ // starting point was outside -> everything is outside the boundary -> nothing is clipped -> add black side
|
|
|
+ // to result mesh unchanged
|
|
|
+ if( !startedInside )
|
|
|
+ {
|
|
|
+ outvertcnt.push_back(blackside.size());
|
|
|
+ outvert.insert(outvert.end(), blackside.begin(), blackside.end());
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ // starting point was inside the boundary -> everything is inside the boundary -> nothing is spared from the
|
|
|
+ // clipping -> nothing left to add to the result mesh
|
|
|
+ continue;
|
|
|
}
|
|
|
- entered_volume_flag = false;
|
|
|
- ++newcount;
|
|
|
}
|
|
|
- else { // no intersection with plane or parallel; e0,e1 are on the bad side
|
|
|
-
|
|
|
- // did we just pass the boundary line to the poly bounding?
|
|
|
- if (is_boundary_intersection) {
|
|
|
-
|
|
|
- // and are now outside the clipping boundary?
|
|
|
- if (is_outside_boundary) {
|
|
|
- // in this case, get the point where the clipping boundary
|
|
|
- // was entered first. Then, get the point where the clipping
|
|
|
- // boundary volume was left! These two points with the plane
|
|
|
- // normal form another plane that intersects the clipping
|
|
|
- // volume. There are two ways to get from the first to the
|
|
|
- // second point along the intersection curve, try to pick the
|
|
|
- // one that lies within the current polygon.
|
|
|
-
|
|
|
- // TODO this approach doesn't handle all cases
|
|
|
-
|
|
|
- // ...
|
|
|
-
|
|
|
- IfcFloat d = 1e20;
|
|
|
- IfcVector3 vclosest;
|
|
|
- BOOST_FOREACH(const IfcVector3& v, intersected_boundary_points) {
|
|
|
- const IfcFloat dn = (v-e1_plane).SquareLength();
|
|
|
- if (dn < d) {
|
|
|
- d = dn;
|
|
|
- vclosest = v;
|
|
|
- }
|
|
|
- }
|
|
|
|
|
|
- vclosest = proj_inv * vclosest;
|
|
|
- if(entered_volume_flag) {
|
|
|
- const IfcVector3& fix_point = vclosest + ((p - vclosest) * n) * n;
|
|
|
- outvert.push_back(fix_point);
|
|
|
- ++newcount;
|
|
|
+ // determine the direction in which we're marching along the boundary polygon. If the src poly is faced upwards
|
|
|
+ // and the boundary is also winded this way, we need to march *backwards* on the boundary.
|
|
|
+ const IfcVector3 polyNormal = IfcMatrix3(proj) * TempMesh::ComputePolygonNormal(blackside.data(), blackside.size());
|
|
|
+ bool marchBackwardsOnBoundary = (profileNormal * polyNormal) >= 0.0;
|
|
|
+
|
|
|
+ // Build closed loops from these intersections. Starting from an intersection leaving the boundary we
|
|
|
+ // walk along the polygon to the next intersection (which should be an IS entering the boundary poly).
|
|
|
+ // From there we walk along the boundary until we hit another intersection leaving the boundary,
|
|
|
+ // walk along the poly to the next IS and so on until we're back at the starting point.
|
|
|
+ // We remove every intersection we "used up", so any remaining intersection is the start of a new loop.
|
|
|
+ while( !intersections.empty() )
|
|
|
+ {
|
|
|
+ std::vector<IfcVector3> resultpoly;
|
|
|
+ size_t currentIntersecIdx = 0;
|
|
|
+
|
|
|
+ while( true )
|
|
|
+ {
|
|
|
+ ai_assert(intersections.size() > currentIntersecIdx + 1);
|
|
|
+ boost::tuple<size_t, IfcVector3, size_t> currintsec = intersections[currentIntersecIdx + 0];
|
|
|
+ boost::tuple<size_t, IfcVector3, size_t> nextintsec = intersections[currentIntersecIdx + 1];
|
|
|
+ intersections.erase(intersections.begin() + currentIntersecIdx, intersections.begin() + currentIntersecIdx + 2);
|
|
|
+
|
|
|
+ // we start with an in->out intersection
|
|
|
+ resultpoly.push_back(currintsec.get<1>());
|
|
|
+ // climb along the polygon to the next intersection, which should be an out->in
|
|
|
+ size_t numPolyPoints = (currintsec.get<0>() > nextintsec.get<0>() ? blackside.size() : 0)
|
|
|
+ + nextintsec.get<0>() - currintsec.get<0>();
|
|
|
+ for( size_t a = 1; a <= numPolyPoints; ++a )
|
|
|
+ resultpoly.push_back(blackside[(currintsec.get<0>() + a) % blackside.size()]);
|
|
|
+ // put the out->in intersection
|
|
|
+ resultpoly.push_back(nextintsec.get<1>());
|
|
|
+
|
|
|
+ // generate segments along the boundary polygon that lie in the poly's plane until we hit another intersection
|
|
|
+ IfcVector3 startingPoint = proj * nextintsec.get<1>();
|
|
|
+ size_t currentBoundaryEdgeIdx = (nextintsec.get<2>() + (marchBackwardsOnBoundary ? 1 : 0)) % profile->verts.size();
|
|
|
+ size_t nextIntsecIdx = SIZE_MAX;
|
|
|
+ while( nextIntsecIdx == SIZE_MAX )
|
|
|
+ {
|
|
|
+ IfcFloat t = 1e10;
|
|
|
+
|
|
|
+ size_t nextBoundaryEdgeIdx = marchBackwardsOnBoundary ? (currentBoundaryEdgeIdx + profile->verts.size() - 1) : currentBoundaryEdgeIdx + 1;
|
|
|
+ nextBoundaryEdgeIdx %= profile->verts.size();
|
|
|
+ // vertices of the current boundary segments
|
|
|
+ IfcVector3 currBoundaryPoint = profile->verts[currentBoundaryEdgeIdx];
|
|
|
+ IfcVector3 nextBoundaryPoint = profile->verts[nextBoundaryEdgeIdx];
|
|
|
+ // project the two onto the polygon
|
|
|
+ if( std::abs(polyNormal.z) > 1e-5 )
|
|
|
+ {
|
|
|
+ currBoundaryPoint.z = startingPoint.z + (currBoundaryPoint.x - startingPoint.x) * polyNormal.x/polyNormal.z + (currBoundaryPoint.y - startingPoint.y) * polyNormal.y/polyNormal.z;
|
|
|
+ nextBoundaryPoint.z = startingPoint.z + (nextBoundaryPoint.x - startingPoint.x) * polyNormal.x/polyNormal.z + (nextBoundaryPoint.y - startingPoint.y) * polyNormal.y/polyNormal.z;
|
|
|
+ }
|
|
|
|
|
|
- entered_volume_flag = false;
|
|
|
+ // build a direction that goes along the boundary border but lies in the poly plane
|
|
|
+ IfcVector3 boundaryPlaneNormal = ((nextBoundaryPoint - currBoundaryPoint) ^ profileNormal).Normalize();
|
|
|
+ IfcVector3 dirAtPolyPlane = (boundaryPlaneNormal ^ polyNormal).Normalize() * (marchBackwardsOnBoundary ? -1.0 : 1.0);
|
|
|
+ // if we can project the direction to the plane, we can calculate a maximum marching distance along that dir
|
|
|
+ // until we finish that boundary segment and continue on the next
|
|
|
+ if( std::abs(polyNormal.z) > 1e-5 )
|
|
|
+ {
|
|
|
+ t = std::min(t, (nextBoundaryPoint - startingPoint).Length());
|
|
|
}
|
|
|
|
|
|
- outvert.push_back(vclosest);
|
|
|
- ++newcount;
|
|
|
+ // check if the direction hits the loop start - if yes, we got a poly to output
|
|
|
+ IfcVector3 dirToThatPoint = proj * resultpoly.front() - startingPoint;
|
|
|
+ IfcFloat tpt = dirToThatPoint * dirAtPolyPlane;
|
|
|
+ if( tpt > -1e-6 && tpt <= t && (dirToThatPoint - tpt * dirAtPolyPlane).SquareLength() < 1e-10 )
|
|
|
+ {
|
|
|
+ nextIntsecIdx = intersections.size(); // dirty hack to end marching along the boundary and signal the end of the loop
|
|
|
+ t = tpt;
|
|
|
+ }
|
|
|
|
|
|
- //outvert.push_back(e1);
|
|
|
- //++newcount;
|
|
|
- }
|
|
|
- else {
|
|
|
- entered_volume_flag = true;
|
|
|
-
|
|
|
- // we just entered the clipping boundary. Record the point
|
|
|
- // and the segment where we entered and also generate this point.
|
|
|
- //last_intersected_boundary_segment = intersected_boundary_segments.front();
|
|
|
- //last_intersected_boundary_point = intersected_boundary_points.front();
|
|
|
-
|
|
|
- outvert.push_back(e0);
|
|
|
- ++newcount;
|
|
|
-
|
|
|
- IfcFloat d = 1e20;
|
|
|
- IfcVector3 vclosest;
|
|
|
- BOOST_FOREACH(const IfcVector3& v, intersected_boundary_points) {
|
|
|
- const IfcFloat dn = (v-e0_plane).SquareLength();
|
|
|
- if (dn < d) {
|
|
|
- d = dn;
|
|
|
- vclosest = v;
|
|
|
+ // also check if the direction hits any in->out intersections earlier. If we hit one, we can switch back
|
|
|
+ // to marching along the poly border from that intersection point
|
|
|
+ for( size_t a = 0; a < intersections.size(); a += 2 )
|
|
|
+ {
|
|
|
+ dirToThatPoint = proj * intersections[a].get<1>() - startingPoint;
|
|
|
+ tpt = dirToThatPoint * dirAtPolyPlane;
|
|
|
+ if( tpt > -1e-6 && tpt <= t && (dirToThatPoint - tpt * dirAtPolyPlane).SquareLength() < 1e-10 )
|
|
|
+ {
|
|
|
+ nextIntsecIdx = a; // switch back to poly and march on from this in->out intersection
|
|
|
+ t = tpt;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- enter_volume = proj_inv * vclosest;
|
|
|
- outvert.push_back(enter_volume);
|
|
|
- ++newcount;
|
|
|
- }
|
|
|
- }
|
|
|
- // if not, we just keep the vertex
|
|
|
- else if (is_outside_boundary) {
|
|
|
- outvert.push_back(e0);
|
|
|
- ++newcount;
|
|
|
-
|
|
|
- entered_volume_flag = false;
|
|
|
- }
|
|
|
- }
|
|
|
+ // if we keep marching on the boundary, put the segment end point to the result poly and well... keep marching
|
|
|
+ if( nextIntsecIdx == SIZE_MAX )
|
|
|
+ {
|
|
|
+ resultpoly.push_back(proj_inv * nextBoundaryPoint);
|
|
|
+ currentBoundaryEdgeIdx = nextBoundaryEdgeIdx;
|
|
|
+ startingPoint = nextBoundaryPoint;
|
|
|
+ }
|
|
|
|
|
|
- was_outside_boundary = is_outside_boundary;
|
|
|
- extra_point_flag = false;
|
|
|
- }
|
|
|
-
|
|
|
- if (!newcount) {
|
|
|
- continue;
|
|
|
- }
|
|
|
-
|
|
|
- IfcVector3 vmin,vmax;
|
|
|
- ArrayBounds(&*(outvert.end()-newcount),newcount,vmin,vmax);
|
|
|
+ // quick endless loop check
|
|
|
+ if( resultpoly.size() > blackside.size() + profile->verts.size() )
|
|
|
+ {
|
|
|
+ IFCImporter::LogError("Encountered endless loop while clipping polygon against poly-bounded half space.");
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- // filter our IfcFloat points - those may happen if a point lies
|
|
|
- // directly on the intersection line. However, due to IfcFloat
|
|
|
- // precision a bitwise comparison is not feasible to detect
|
|
|
- // this case.
|
|
|
- const IfcFloat epsilon = (vmax-vmin).SquareLength() / 1e6f;
|
|
|
- FuzzyVectorCompare fz(epsilon);
|
|
|
+ // we're back on the poly - if this is the intersection we started from, we got a closed loop.
|
|
|
+ if( nextIntsecIdx >= intersections.size() )
|
|
|
+ {
|
|
|
+ break;
|
|
|
+ }
|
|
|
|
|
|
- std::vector<IfcVector3>::iterator e = std::unique( outvert.end()-newcount, outvert.end(), fz );
|
|
|
+ // otherwise it's another intersection. Continue marching from there.
|
|
|
+ currentIntersecIdx = nextIntsecIdx;
|
|
|
+ }
|
|
|
|
|
|
- if (e != outvert.end()) {
|
|
|
- newcount -= static_cast<unsigned int>(std::distance(e,outvert.end()));
|
|
|
- outvert.erase(e,outvert.end());
|
|
|
- }
|
|
|
- if (fz(*( outvert.end()-newcount),outvert.back())) {
|
|
|
- outvert.pop_back();
|
|
|
- --newcount;
|
|
|
- }
|
|
|
- if(newcount > 2) {
|
|
|
- result.vertcnt.push_back(newcount);
|
|
|
- }
|
|
|
- else while(newcount-->0) {
|
|
|
- result.verts.pop_back();
|
|
|
+ WritePolygon(resultpoly, result);
|
|
|
+ }
|
|
|
}
|
|
|
-
|
|
|
}
|
|
|
IFCImporter::LogDebug("generating CSG geometry by plane clipping with polygonal bounding (IfcBooleanClippingResult)");
|
|
|
}
|