From 11a22b671ba1152865fa16c21eb4dc0ea1eb19c2 Mon Sep 17 00:00:00 2001 From: aramis_acg Date: Tue, 16 Oct 2012 19:51:00 +0000 Subject: [PATCH] - IFC: revamp binary subtraction and opening generation logic, which now supports 90deg rotated opening proxies. The new version adds extra cleanup steps to prepare the data for processing by clipper. It also has a slightly refactored code base. git-svn-id: https://assimp.svn.sourceforge.net/svnroot/assimp/trunk@1305 67173fc5-114c-0410-ac8e-9d2fd5bffc1f --- code/IFCGeometry.cpp | 272 ++++++++++++++++++++++++++++--------------- 1 file changed, 175 insertions(+), 97 deletions(-) diff --git a/code/IFCGeometry.cpp b/code/IFCGeometry.cpp index 351f0a86b..070523976 100644 --- a/code/IFCGeometry.cpp +++ b/code/IFCGeometry.cpp @@ -1143,7 +1143,6 @@ void QuadrifyPart(const IfcVector2& pmin, const IfcVector2& pmax, XYSortedField& void InsertWindowContours(const std::vector< BoundingBox >& bbs, const std::vector< std::vector >& contours, const std::vector& openings, - const std::vector& nors, const IfcMatrix3& minv, const IfcVector2& scale, const IfcVector2& offset, @@ -1272,7 +1271,9 @@ void InsertWindowContours(const std::vector< BoundingBox >& bbs, } // ------------------------------------------------------------------------------------------------ -void MergeContours (const std::vector& a, const std::vector& b, ClipperLib::ExPolygons& out) +void MergeWindowContours (const std::vector& a, + const std::vector& b, + ClipperLib::ExPolygons& out) { ClipperLib::Clipper clipper; ClipperLib::Polygon clip; @@ -1301,7 +1302,137 @@ void MergeContours (const std::vector& a, const std::vector& openings,const std::vector& nors, TempMesh& curmesh) +void CleanupWindowContours(std::vector< std::vector >& contours) +{ + std::vector scratch; + + // use polyclipper to clean up window contours as well + try { + BOOST_FOREACH(std::vector& contour, contours) { + ClipperLib::Polygon subject; + ClipperLib::Clipper clipper; + ClipperLib::ExPolygons clipped; + + BOOST_FOREACH(const IfcVector2& pip, contour) { + subject.push_back(ClipperLib::IntPoint( to_int64(pip.x), to_int64(pip.y) )); + } + + clipper.AddPolygon(subject,ClipperLib::ptSubject); + clipper.Execute(ClipperLib::ctUnion,clipped,ClipperLib::pftNonZero,ClipperLib::pftNonZero); + + // this should yield only one polygon or something went wrong + if (clipped.size() != 1) { + + // empty polygon? drop the contour altogether + if(clipped.empty()) { + contour.clear(); + continue; + } + + // else: take only the first ... + IFCImporter::LogError("error during polygon clipping, window contour is not convex"); + } + + scratch.clear(); + BOOST_FOREACH(const ClipperLib::IntPoint& point, clipped[0].outer) { + scratch.push_back( IfcVector2(from_int64(point.X), from_int64(point.Y))); + } + contour.swap(scratch); + } + } + catch (const char* sx) { + IFCImporter::LogError("error during polygon clipping, window shape may be wrong: (Clipper: " + + std::string(sx) + ")"); + } +} + +// ------------------------------------------------------------------------------------------------ +void CleanupOuterContour(const std::vector& contour_flat, TempMesh& curmesh, + const IfcMatrix3& minv, + const IfcVector2& vmin, + const IfcVector2& vmax, + IfcFloat coord, + const std::vector& outflat) +{ + std::vector vold; + std::vector iold; + + vold.reserve(outflat.size()); + iold.reserve(outflat.size() / 4); + + // Fix the outer contour using polyclipper + try { + + ClipperLib::Polygon subject; + ClipperLib::Clipper clipper; + ClipperLib::ExPolygons clipped; + + ClipperLib::Polygon clip; + clip.reserve(contour_flat.size()); + BOOST_FOREACH(const IfcVector2& pip, contour_flat) { + clip.push_back(ClipperLib::IntPoint( to_int64(pip.x), to_int64(pip.y) )); + } + + if (!ClipperLib::Orientation(clip)) { + std::reverse(clip.begin(), clip.end()); + } + + // We need to run polyclipper on every single quad -- we can't run it one all + // of them at once or it would merge them all together which would undo all + // previous steps + subject.reserve(4); + size_t cnt = 0; + BOOST_FOREACH(const IfcVector2& pip, outflat) { + subject.push_back(ClipperLib::IntPoint( to_int64(pip.x), to_int64(pip.y) )); + if (!(++cnt % 4)) { + if (!ClipperLib::Orientation(subject)) { + std::reverse(subject.begin(), subject.end()); + } + + clipper.AddPolygon(subject,ClipperLib::ptSubject); + clipper.AddPolygon(clip,ClipperLib::ptClip); + + clipper.Execute(ClipperLib::ctIntersection,clipped,ClipperLib::pftNonZero,ClipperLib::pftNonZero); + + BOOST_FOREACH(const ClipperLib::ExPolygon& ex, clipped) { + iold.push_back(ex.outer.size()); + BOOST_FOREACH(const ClipperLib::IntPoint& point, ex.outer) { + vold.push_back( minv * IfcVector3( + vmin.x + from_int64(point.X) * vmax.x, + vmin.y + from_int64(point.Y) * vmax.y, + coord)); + } + } + + subject.clear(); + clipped.clear(); + clipper.Clear(); + } + } + + assert(!(cnt % 4)); + } + catch (const char* sx) { + IFCImporter::LogError("Ifc: error during polygon clipping, wall contour line may be wrong: (Clipper: " + + std::string(sx) + ")"); + + iold.resize(outflat.size()/4,4); + + BOOST_FOREACH(const IfcVector2& vproj, outflat) { + const IfcVector3 v3 = minv * IfcVector3(vmin.x + vproj.x * vmax.x, vmin.y + vproj.y * vmax.y,coord); + vold.push_back(v3); + } + } + + // undo the projection, generate output quads + std::swap(vold,curmesh.verts); + std::swap(iold,curmesh.vertcnt); +} + +// ------------------------------------------------------------------------------------------------ +bool TryAddOpenings_Quadrulate(const std::vector& openings, + const std::vector& nors, + TempMesh& curmesh) { std::vector& out = curmesh.verts; @@ -1360,12 +1491,10 @@ bool TryAddOpenings_Quadrulate(const std::vector& openings,const st BOOST_FOREACH(const TempOpening& t,openings) { const IfcVector3& outernor = nors[c++]; const IfcFloat dot = nor * outernor; - if (fabs(dot)<1.f-1e-6f) { - continue; - } - const std::vector& va = t.profileMesh->verts; - if(va.size() <= 2) { + std::vector profile_verts = t.profileMesh->verts; + std::vector profile_vertcnts = t.profileMesh->vertcnt; + if(profile_verts.size() <= 2) { continue; } @@ -1373,22 +1502,38 @@ bool TryAddOpenings_Quadrulate(const std::vector& openings,const st MinMaxChooser()(vpmin,vpmax); std::vector contour; + for (size_t f = 0, vi_total = 0, fend = profile_vertcnts.size(); f < fend; ++f) { - BOOST_FOREACH(const IfcVector3& x, t.profileMesh->verts) { - const IfcVector3 v = m * x; - - IfcVector2 vv(v.x, v.y); + const IfcVector3& face_nor = ((profile_verts[vi_total+2] - profile_verts[vi_total]) ^ + (profile_verts[vi_total+1] - profile_verts[vi_total])).Normalize(); - // rescale - vv.x = (vv.x - vmin.x) / vmax.x; - vv.y = (vv.y - vmin.y) / vmax.y; + const IfcFloat dot_face_nor = nor * face_nor; + if (fabs(dot_face_nor) < 0.5) { + vi_total += profile_vertcnts[f]; + continue; + } - vpmin = std::min(vpmin,vv); - vpmax = std::max(vpmax,vv); + for (unsigned int vi = 0, vend = profile_vertcnts[f]; vi < vend; ++vi, ++vi_total) { + const IfcVector3& x = profile_verts[vi_total]; - contour.push_back(vv); + const IfcVector3 v = m * x; + IfcVector2 vv(v.x, v.y); + + // rescale + vv.x = (vv.x - vmin.x) / vmax.x; + vv.y = (vv.y - vmin.y) / vmax.y; + + vpmin = std::min(vpmin,vv); + vpmax = std::max(vpmax,vv); + + contour.push_back(vv); + } } - + + if(contour.size() <= 2) { + continue; + } + BoundingBox bb = BoundingBox(vpmin,vpmax); // see if this BB intersects any other, in which case we could not use the Quadrify() @@ -1405,11 +1550,11 @@ bool TryAddOpenings_Quadrulate(const std::vector& openings,const st const std::vector& other = contours[std::distance(bbs.begin(),it)]; ClipperLib::ExPolygons poly; - MergeContours(contour, other, poly); + MergeWindowContours(contour, other, poly); if (poly.size() > 1) { IFCImporter::LogWarn("cannot use quadrify algorithm to generate wall openings due to " - "bounding box overlaps, using poly2tri fallback"); + "bounding box overlaps, using poly2tri fallback method"); return TryAddOpenings_Poly2Tri(openings, nors, curmesh); } else if (poly.size() == 0) { @@ -1459,81 +1604,10 @@ bool TryAddOpenings_Quadrulate(const std::vector& openings,const st QuadrifyPart(IfcVector2(0.f,0.f),IfcVector2(1.f,1.f),field,bbs,outflat); ai_assert(!(outflat.size() % 4)); - std::vector vold; - std::vector iold; + CleanupOuterContour(contour_flat, curmesh, minv, vmin, vmax, coord, outflat); + CleanupWindowContours(contours); - vold.reserve(outflat.size()); - iold.reserve(outflat.size() / 4); - - // Fix the outer contour using polyclipper - try { - - ClipperLib::Polygon subject; - ClipperLib::Clipper clipper; - ClipperLib::ExPolygons clipped; - - ClipperLib::Polygon clip; - clip.reserve(contour_flat.size()); - BOOST_FOREACH(const IfcVector2& pip, contour_flat) { - clip.push_back(ClipperLib::IntPoint( to_int64(pip.x), to_int64(pip.y) )); - } - - if (!ClipperLib::Orientation(clip)) { - std::reverse(clip.begin(), clip.end()); - } - - // We need to run polyclipper on every single quad -- we can't run it one all - // of them at once or it would merge them all together which would undo all - // previous steps - subject.reserve(4); - size_t cnt = 0; - BOOST_FOREACH(const IfcVector2& pip, outflat) { - subject.push_back(ClipperLib::IntPoint( to_int64(pip.x), to_int64(pip.y) )); - if (!(++cnt % 4)) { - if (!ClipperLib::Orientation(subject)) { - std::reverse(subject.begin(), subject.end()); - } - - clipper.AddPolygon(subject,ClipperLib::ptSubject); - clipper.AddPolygon(clip,ClipperLib::ptClip); - - clipper.Execute(ClipperLib::ctIntersection,clipped,ClipperLib::pftNonZero,ClipperLib::pftNonZero); - - BOOST_FOREACH(const ClipperLib::ExPolygon& ex, clipped) { - iold.push_back(ex.outer.size()); - BOOST_FOREACH(const ClipperLib::IntPoint& point, ex.outer) { - vold.push_back( minv * IfcVector3( - vmin.x + from_int64(point.X) * vmax.x, - vmin.y + from_int64(point.Y) * vmax.y, - coord)); - } - } - - subject.clear(); - clipped.clear(); - clipper.Clear(); - } - } - - assert(!(cnt % 4)); - } - catch (const char* sx) { - IFCImporter::LogError("Ifc: error during polygon clipping, contour line may be wrong: (Clipper: " - + std::string(sx) + ")"); - - iold.resize(outflat.size()/4,4); - - BOOST_FOREACH(const IfcVector2& vproj, outflat) { - const IfcVector3 v3 = minv * IfcVector3(vmin.x + vproj.x * vmax.x, vmin.y + vproj.y * vmax.y,coord); - vold.push_back(v3); - } - } - - // undo the projection, generate output quads - std::swap(vold,curmesh.verts); - std::swap(iold,curmesh.vertcnt); - - InsertWindowContours(bbs,contours,openings, nors,minv,vmax, vmin, coord, curmesh); + InsertWindowContours(bbs,contours,openings, minv,vmax, vmin, coord, curmesh); return true; } @@ -1672,6 +1746,9 @@ void ProcessSweptAreaSolid(const IfcSweptAreaSolid& swept, TempMesh& meshout, Co // In this case we don't extrude the surface yet, just keep the profile and transform it correctly if(conv.collect_openings) { boost::shared_ptr meshtmp(new TempMesh()); + ProcessExtrudedAreaSolid(*solid,*meshtmp,conv); + + /* ProcessProfile(swept.SweptArea,*meshtmp,conv); IfcMatrix4 m; @@ -1679,8 +1756,9 @@ void ProcessSweptAreaSolid(const IfcSweptAreaSolid& swept, TempMesh& meshout, Co meshtmp->Transform(m); IfcVector3 dir; - ConvertDirection(dir,solid->ExtrudedDirection); - conv.collect_openings->push_back(TempOpening(solid, IfcMatrix3(m) * (dir*static_cast(solid->Depth)),meshtmp)); + ConvertDirection(dir,solid->ExtrudedDirection); */ + conv.collect_openings->push_back(TempOpening(solid,IfcVector3(0,0,0) + /* IfcMatrix3(m) * (dir*static_cast(solid->Depth)) */,meshtmp)); return; }