Merge pull request #4344 from revu-design/bensewell-fixifcopening4343
IFC Reading: Fix opening reading.pull/4338/head^2
commit
d45af75a5e
|
@ -699,7 +699,7 @@ void ProcessBooleanExtrudedAreaSolidDifference(const Schema_2x3::IfcExtrudedArea
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
GenerateOpenings(openings, std::vector<IfcVector3>(1, IfcVector3(1, 0, 0)), temp, false, true);
|
GenerateOpenings(openings, temp, false, true);
|
||||||
result.Append(temp);
|
result.Append(temp);
|
||||||
|
|
||||||
vit += pcount;
|
vit += pcount;
|
||||||
|
|
|
@ -190,7 +190,7 @@ void ProcessPolygonBoundaries(TempMesh& result, const TempMesh& inmesh, size_t m
|
||||||
std::copy(outer_vit, outer_vit+outer_polygon_size,
|
std::copy(outer_vit, outer_vit+outer_polygon_size,
|
||||||
std::back_inserter(temp.mVerts));
|
std::back_inserter(temp.mVerts));
|
||||||
|
|
||||||
GenerateOpenings(fake_openings, normals, temp, false, false);
|
GenerateOpenings(fake_openings, temp, false, false);
|
||||||
result.Append(temp);
|
result.Append(temp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -529,6 +529,31 @@ IfcMatrix3 DerivePlaneCoordinateSpace(const TempMesh& curmesh, bool& ok, IfcVect
|
||||||
return m;
|
return m;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const auto closeDistance = 1e-6;
|
||||||
|
|
||||||
|
bool areClose(Schema_2x3::IfcCartesianPoint pt1,Schema_2x3::IfcCartesianPoint pt2) {
|
||||||
|
if(pt1.Coordinates.size() != pt2.Coordinates.size())
|
||||||
|
{
|
||||||
|
IFCImporter::LogWarn("unable to compare differently-dimensioned points");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
auto coord1 = pt1.Coordinates.begin();
|
||||||
|
auto coord2 = pt2.Coordinates.begin();
|
||||||
|
// we're just testing each dimension separately rather than doing euclidean distance, as we're
|
||||||
|
// looking for very close coordinates
|
||||||
|
for(; coord1 != pt1.Coordinates.end(); coord1++,coord2++)
|
||||||
|
{
|
||||||
|
if(std::fabs(*coord1 - *coord2) > closeDistance)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool areClose(IfcVector3 pt1,IfcVector3 pt2) {
|
||||||
|
return (std::fabs(pt1.x - pt2.x) < closeDistance &&
|
||||||
|
std::fabs(pt1.y - pt2.y) < closeDistance &&
|
||||||
|
std::fabs(pt1.z - pt2.z) < closeDistance);
|
||||||
|
}
|
||||||
// Extrudes the given polygon along the direction, converts it into an opening or applies all openings as necessary.
|
// Extrudes the given polygon along the direction, converts it into an opening or applies all openings as necessary.
|
||||||
void ProcessExtrudedArea(const Schema_2x3::IfcExtrudedAreaSolid& solid, const TempMesh& curve,
|
void ProcessExtrudedArea(const Schema_2x3::IfcExtrudedAreaSolid& solid, const TempMesh& curve,
|
||||||
const IfcVector3& extrusionDir, TempMesh& result, ConversionData &conv, bool collect_openings)
|
const IfcVector3& extrusionDir, TempMesh& result, ConversionData &conv, bool collect_openings)
|
||||||
|
@ -592,7 +617,21 @@ void ProcessExtrudedArea(const Schema_2x3::IfcExtrudedAreaSolid& solid, const Te
|
||||||
nors.push_back(IfcVector3());
|
nors.push_back(IfcVector3());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
nors.push_back(((bounds.mVerts[2] - bounds.mVerts[0]) ^ (bounds.mVerts[1] - bounds.mVerts[0])).Normalize());
|
auto nor = ((bounds.mVerts[2] - bounds.mVerts[0]) ^ (bounds.mVerts[1] - bounds.mVerts[0])).Normalize();
|
||||||
|
auto vI0 = bounds.mVertcnt[0];
|
||||||
|
for(size_t faceI = 0; faceI < bounds.mVertcnt.size(); faceI++)
|
||||||
|
{
|
||||||
|
if(bounds.mVertcnt[faceI] >= 3) {
|
||||||
|
// do a check that this is at least parallel to the base plane
|
||||||
|
auto nor2 = ((bounds.mVerts[vI0 + 2] - bounds.mVerts[vI0]) ^ (bounds.mVerts[vI0 + 1] - bounds.mVerts[vI0])).Normalize();
|
||||||
|
if(!areClose(nor,nor2)) {
|
||||||
|
std::stringstream msg;
|
||||||
|
msg << "Face " << faceI << " is not parallel with face 0 - opening on entity " << solid.GetID();
|
||||||
|
IFCImporter::LogWarn(msg.str().c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
nors.push_back(nor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -613,7 +652,7 @@ void ProcessExtrudedArea(const Schema_2x3::IfcExtrudedAreaSolid& solid, const Te
|
||||||
out.push_back(in[i] + dir);
|
out.push_back(in[i] + dir);
|
||||||
|
|
||||||
if( openings ) {
|
if( openings ) {
|
||||||
if( (in[i] - in[next]).Length() > diag * 0.1 && GenerateOpenings(*conv.apply_openings, nors, temp, true, true, dir) ) {
|
if( (in[i] - in[next]).Length() > diag * 0.1 && GenerateOpenings(*conv.apply_openings, temp, true, true, dir) ) {
|
||||||
++sides_with_openings;
|
++sides_with_openings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -622,31 +661,33 @@ void ProcessExtrudedArea(const Schema_2x3::IfcExtrudedAreaSolid& solid, const Te
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if( openings ) {
|
if(openings) {
|
||||||
for(TempOpening& opening : *conv.apply_openings) {
|
for(TempOpening& opening : *conv.apply_openings) {
|
||||||
if( !opening.wallPoints.empty() ) {
|
if(!opening.wallPoints.empty()) {
|
||||||
IFCImporter::LogError("failed to generate all window caps");
|
std::stringstream msg;
|
||||||
|
msg << "failed to generate all window caps on ID " << (int)solid.GetID();
|
||||||
|
IFCImporter::LogError(msg.str().c_str());
|
||||||
}
|
}
|
||||||
opening.wallPoints.clear();
|
opening.wallPoints.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t sides_with_v_openings = 0;
|
size_t sides_with_v_openings = 0;
|
||||||
if( has_area ) {
|
if(has_area) {
|
||||||
|
|
||||||
for( size_t n = 0; n < 2; ++n ) {
|
for(size_t n = 0; n < 2; ++n) {
|
||||||
if( n > 0 ) {
|
if(n > 0) {
|
||||||
for( size_t i = 0; i < in.size(); ++i )
|
for(size_t i = 0; i < in.size(); ++i)
|
||||||
out.push_back(in[i] + dir);
|
out.push_back(in[i] + dir);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
for( size_t i = in.size(); i--; )
|
for(size_t i = in.size(); i--; )
|
||||||
out.push_back(in[i]);
|
out.push_back(in[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
curmesh.mVertcnt.push_back(static_cast<unsigned int>(in.size()));
|
curmesh.mVertcnt.push_back(static_cast<unsigned int>(in.size()));
|
||||||
if( openings && in.size() > 2 ) {
|
if(openings && in.size() > 2) {
|
||||||
if( GenerateOpenings(*conv.apply_openings, nors, temp, true, true, dir) ) {
|
if(GenerateOpenings(*conv.apply_openings,temp,true,true,dir)) {
|
||||||
++sides_with_v_openings;
|
++sides_with_v_openings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -656,8 +697,10 @@ void ProcessExtrudedArea(const Schema_2x3::IfcExtrudedAreaSolid& solid, const Te
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if( openings && (sides_with_openings == 1 || sides_with_v_openings == 2 ) ) {
|
if (openings && (sides_with_openings == 1 || sides_with_v_openings == 2)) {
|
||||||
IFCImporter::LogWarn("failed to resolve all openings, presumably their topology is not supported by Assimp");
|
std::stringstream msg;
|
||||||
|
msg << "failed to resolve all openings, presumably their topology is not supported by Assimp - ID " << solid.GetID() << " sides_with_openings " << sides_with_openings << " sides_with_v_openings " << sides_with_v_openings;
|
||||||
|
IFCImporter::LogWarn(msg.str().c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
IFCImporter::LogVerboseDebug("generate mesh procedurally by extrusion (IfcExtrudedAreaSolid)");
|
IFCImporter::LogVerboseDebug("generate mesh procedurally by extrusion (IfcExtrudedAreaSolid)");
|
||||||
|
@ -781,7 +824,9 @@ bool ProcessGeometricItem(const Schema_2x3::IfcRepresentationItem& geo, unsigned
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
IFCImporter::LogWarn("skipping unknown IfcGeometricRepresentationItem entity, type is ", geo.GetClassName());
|
std::stringstream toLog;
|
||||||
|
toLog << "skipping unknown IfcGeometricRepresentationItem entity, type is " << geo.GetClassName() << " id is " << geo.GetID();
|
||||||
|
IFCImporter::LogWarn(toLog.str().c_str());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -58,6 +58,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
|
#include <forward_list>
|
||||||
|
#include <deque>
|
||||||
|
|
||||||
namespace Assimp {
|
namespace Assimp {
|
||||||
namespace IFC {
|
namespace IFC {
|
||||||
|
@ -73,7 +75,7 @@ namespace Assimp {
|
||||||
|
|
||||||
|
|
||||||
// fallback method to generate wall openings
|
// fallback method to generate wall openings
|
||||||
bool TryAddOpenings_Poly2Tri(const std::vector<TempOpening>& openings,const std::vector<IfcVector3>& nors,
|
bool TryAddOpenings_Poly2Tri(const std::vector<TempOpening>& openings,
|
||||||
TempMesh& curmesh);
|
TempMesh& curmesh);
|
||||||
|
|
||||||
|
|
||||||
|
@ -1140,7 +1142,6 @@ IfcMatrix4 ProjectOntoPlane(std::vector<IfcVector2>& out_contour, const TempMesh
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
bool GenerateOpenings(std::vector<TempOpening>& openings,
|
bool GenerateOpenings(std::vector<TempOpening>& openings,
|
||||||
const std::vector<IfcVector3>& nors,
|
|
||||||
TempMesh& curmesh,
|
TempMesh& curmesh,
|
||||||
bool check_intersection,
|
bool check_intersection,
|
||||||
bool generate_connection_geometry,
|
bool generate_connection_geometry,
|
||||||
|
@ -1340,7 +1341,7 @@ bool GenerateOpenings(std::vector<TempOpening>& openings,
|
||||||
MergeWindowContours(temp_contour, other, poly);
|
MergeWindowContours(temp_contour, other, poly);
|
||||||
|
|
||||||
if (poly.size() > 1) {
|
if (poly.size() > 1) {
|
||||||
return TryAddOpenings_Poly2Tri(openings, nors, curmesh);
|
return TryAddOpenings_Poly2Tri(openings, curmesh);
|
||||||
}
|
}
|
||||||
else if (poly.size() == 0) {
|
else if (poly.size() == 0) {
|
||||||
IFCImporter::LogWarn("ignoring duplicate opening");
|
IFCImporter::LogWarn("ignoring duplicate opening");
|
||||||
|
@ -1427,8 +1428,289 @@ bool GenerateOpenings(std::vector<TempOpening>& openings,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<IfcVector2> GetContourInPlane2D(std::shared_ptr<TempMesh> mesh,IfcMatrix3 planeSpace,
|
||||||
|
IfcVector3 planeNor,IfcFloat planeOffset,
|
||||||
|
IfcVector3 extrusionDir,IfcVector3& wall_extrusion,bool& first,bool& ok) {
|
||||||
|
std::vector<IfcVector2> contour;
|
||||||
|
|
||||||
|
const auto outernor = ((mesh->mVerts[2] - mesh->mVerts[0]) ^ (mesh->mVerts[1] - mesh->mVerts[0])).Normalize();
|
||||||
|
const IfcFloat dot = planeNor * outernor;
|
||||||
|
if(std::fabs(dot) < 1.f - 1e-6f) {
|
||||||
|
std::stringstream msg;
|
||||||
|
msg << "Skipping: Unaligned opening (" << planeNor.x << ", " << planeNor.y << ", " << planeNor.z << ")";
|
||||||
|
msg << " . ( " << outernor.x << ", " << outernor.y << ", " << outernor.z << ") = " << dot;
|
||||||
|
IFCImporter::LogDebug(msg.str().c_str());
|
||||||
|
ok = false;
|
||||||
|
return contour;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<IfcVector3>& va = mesh->mVerts;
|
||||||
|
if(va.size() <= 2) {
|
||||||
|
std::stringstream msg;
|
||||||
|
msg << "Skipping: Only " << va.size() << " verticies in opening mesh.";
|
||||||
|
IFCImporter::LogDebug(msg.str().c_str());
|
||||||
|
ok = false;
|
||||||
|
return contour;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(const IfcVector3& xx : mesh->mVerts) {
|
||||||
|
IfcVector3 vv = planeSpace * xx,vv_extr = planeSpace * (xx + extrusionDir);
|
||||||
|
|
||||||
|
const bool is_extruded_side = std::fabs(vv.z - planeOffset) > std::fabs(vv_extr.z - planeOffset);
|
||||||
|
if(first) {
|
||||||
|
first = false;
|
||||||
|
if(dot > 0.f) {
|
||||||
|
wall_extrusion = extrusionDir;
|
||||||
|
if(is_extruded_side) {
|
||||||
|
wall_extrusion = -wall_extrusion;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// XXX should not be necessary - but it is. Why? For precision reasons?
|
||||||
|
vv = is_extruded_side ? vv_extr : vv;
|
||||||
|
contour.push_back(IfcVector2(vv.x,vv.y));
|
||||||
|
}
|
||||||
|
ok = true;
|
||||||
|
|
||||||
|
return contour;
|
||||||
|
}
|
||||||
|
|
||||||
|
const float close { 1e-6f };
|
||||||
|
|
||||||
|
static bool isClose(IfcVector2 first,IfcVector2 second) {
|
||||||
|
auto diff = (second - first);
|
||||||
|
return (std::fabs(diff.x) < close && std::fabs(diff.y) < close);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void logSegment(std::pair<IfcVector2,IfcVector2> segment) {
|
||||||
|
std::stringstream msg2;
|
||||||
|
msg2 << " Segment: \n";
|
||||||
|
msg2 << " " << segment.first.x << " " << segment.first.y << " \n";
|
||||||
|
msg2 << " " << segment.second.x << " " << segment.second.y << " \n";
|
||||||
|
IFCImporter::LogInfo(msg2.str().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::vector<IfcVector2>> GetContoursInPlane3D(std::shared_ptr<TempMesh> mesh,IfcMatrix3 planeSpace,
|
||||||
|
IfcFloat planeOffset) {
|
||||||
|
|
||||||
|
{
|
||||||
|
std::stringstream msg;
|
||||||
|
msg << "GetContoursInPlane3D: planeSpace is \n";
|
||||||
|
msg << planeSpace.a1 << " " << planeSpace.a2 << " " << planeSpace.a3 << " " << "\n";
|
||||||
|
msg << planeSpace.b1 << " " << planeSpace.b2 << " " << planeSpace.b3 << " " << "\n";
|
||||||
|
msg << planeSpace.c1 << " " << planeSpace.c2 << " " << planeSpace.c3 << " " << "\n";
|
||||||
|
msg << "\n planeOffset is " << planeOffset;
|
||||||
|
IFCImporter::LogInfo(msg.str().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
// we'll put our line segments in here, and then merge them together into contours later
|
||||||
|
std::deque<std::pair<IfcVector2,IfcVector2>> lineSegments;
|
||||||
|
|
||||||
|
// find the lines giving the intersection of the faces with the plane - we'll work in planeSpace throughout.
|
||||||
|
size_t vI0{ 0 }; // vertex index for first vertex in plane
|
||||||
|
for(auto nVertices : mesh->mVertcnt) { // iterate over faces
|
||||||
|
{
|
||||||
|
std::stringstream msg;
|
||||||
|
msg << "GetContoursInPlane3D: face (transformed) is \n";
|
||||||
|
for(auto vI = vI0; vI < vI0 + nVertices; vI++) {
|
||||||
|
auto v = planeSpace * mesh->mVerts[vI];
|
||||||
|
msg << " " << v.x << " " << v.y << " " << v.z << " " << "\n";
|
||||||
|
}
|
||||||
|
IFCImporter::LogInfo(msg.str().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
if(nVertices <= 2) // not a plane, a point or line
|
||||||
|
{
|
||||||
|
std::stringstream msg;
|
||||||
|
msg << "GetContoursInPlane3D: found point or line when expecting plane (only " << nVertices << " vertices)";
|
||||||
|
IFCImporter::LogWarn(msg.str().c_str());
|
||||||
|
vI0 += nVertices;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto v0 = planeSpace * mesh->mVerts[vI0];
|
||||||
|
|
||||||
|
// now calculate intersections between face and plane
|
||||||
|
IfcVector2 firstPoint;
|
||||||
|
bool gotFirstPoint(false);
|
||||||
|
|
||||||
|
if(std::fabs(v0.z - planeOffset) < close) {
|
||||||
|
// first point is on the plane
|
||||||
|
firstPoint.x = v0.x;
|
||||||
|
firstPoint.y = v0.y;
|
||||||
|
gotFirstPoint = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto vn = v0;
|
||||||
|
for(auto vI = vI0 + 1; vI < vI0 + nVertices; vI++) {
|
||||||
|
auto vp = vn;
|
||||||
|
vn = planeSpace * mesh->mVerts[vI];
|
||||||
|
IfcVector3 intersection;
|
||||||
|
|
||||||
|
if(std::fabs(vn.z - planeOffset) < close) {
|
||||||
|
// on the plane
|
||||||
|
intersection = vn;
|
||||||
|
}
|
||||||
|
else if((vn.z > planeOffset) != (vp.z > planeOffset))
|
||||||
|
{
|
||||||
|
// passes through the plane
|
||||||
|
auto vdir = vn - vp;
|
||||||
|
auto scale = (planeOffset - vp.z) / vdir.z;
|
||||||
|
intersection = vp + scale * vdir;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// nowhere near - move on
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!gotFirstPoint) {
|
||||||
|
if(std::fabs(vp.z - planeOffset) < close) {
|
||||||
|
// just had a second line along the plane
|
||||||
|
firstPoint.x = vp.x;
|
||||||
|
firstPoint.y = vp.y;
|
||||||
|
IfcVector2 secondPoint(intersection.x,intersection.y);
|
||||||
|
auto s = std::pair<IfcVector2,IfcVector2>(firstPoint,secondPoint);
|
||||||
|
logSegment(s);
|
||||||
|
lineSegments.push_back(s);
|
||||||
|
// next firstpoint should be this one
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// store the first intersection point
|
||||||
|
firstPoint.x = intersection.x;
|
||||||
|
firstPoint.y = intersection.y;
|
||||||
|
gotFirstPoint = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// now got the second point, so store the pair
|
||||||
|
IfcVector2 secondPoint(intersection.x,intersection.y);
|
||||||
|
auto s = std::pair<IfcVector2,IfcVector2>(firstPoint,secondPoint);
|
||||||
|
logSegment(s);
|
||||||
|
lineSegments.push_back(s);
|
||||||
|
|
||||||
|
// - note that we don't move onto the next face as a non-convex face can create two or more intersections with a plane
|
||||||
|
gotFirstPoint = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(gotFirstPoint) {
|
||||||
|
IFCImporter::LogWarn("GetContoursInPlane3D: odd number of intersections with plane");
|
||||||
|
}
|
||||||
|
vI0 += nVertices;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
std::stringstream msg;
|
||||||
|
msg << "GetContoursInPlane3D: found " << lineSegments.size() << " line segments:\n";
|
||||||
|
IFCImporter::LogInfo(msg.str().c_str());
|
||||||
|
|
||||||
|
for(auto& s : lineSegments) {
|
||||||
|
logSegment(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// now merge contours until we have the best-looking polygons we can
|
||||||
|
std::vector<Contour> contours;
|
||||||
|
while(!lineSegments.empty()) {
|
||||||
|
// start with a polygon and make the best closed contour we can
|
||||||
|
const auto& firstSeg = lineSegments.front();
|
||||||
|
std::deque<IfcVector2> contour{ firstSeg.first, firstSeg.second };
|
||||||
|
lineSegments.pop_front();
|
||||||
|
bool foundNextPoint{ true };
|
||||||
|
bool closedContour{ false };
|
||||||
|
while(foundNextPoint) {
|
||||||
|
foundNextPoint = false;
|
||||||
|
for(auto nextSeg = lineSegments.begin(); nextSeg != lineSegments.end(); nextSeg++) {
|
||||||
|
// see if we can match up both ends - in which case we've closed the contour
|
||||||
|
if((isClose(contour.front(),nextSeg->first) && isClose(contour.back(),nextSeg->second)) ||
|
||||||
|
(isClose(contour.back(),nextSeg->first) && isClose(contour.front(),nextSeg->second))
|
||||||
|
) {
|
||||||
|
lineSegments.erase(nextSeg);
|
||||||
|
closedContour = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// otherwise, see if we can match up either end
|
||||||
|
foundNextPoint = true;
|
||||||
|
if(isClose(contour.front(),nextSeg->first)) {
|
||||||
|
contour.push_front(nextSeg->second);
|
||||||
|
}
|
||||||
|
else if(isClose(contour.front(),nextSeg->second)) {
|
||||||
|
contour.push_front(nextSeg->first);
|
||||||
|
}
|
||||||
|
else if(isClose(contour.back(),nextSeg->first)) {
|
||||||
|
contour.push_back(nextSeg->second);
|
||||||
|
}
|
||||||
|
else if(isClose(contour.back(),nextSeg->second)) {
|
||||||
|
contour.push_back(nextSeg->first);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
foundNextPoint = false;
|
||||||
|
}
|
||||||
|
if(foundNextPoint) {
|
||||||
|
lineSegments.erase(nextSeg);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!closedContour) {
|
||||||
|
IFCImporter::LogWarn("GetContoursInPlane3D: did not close contour");
|
||||||
|
}
|
||||||
|
|
||||||
|
// now add the contour if we can
|
||||||
|
if(contour.size() <= 2) {
|
||||||
|
IFCImporter::LogWarn("GetContoursInPlane3D: discarding line/point contour");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Contour c{};
|
||||||
|
for(auto p : contour)
|
||||||
|
{
|
||||||
|
c.push_back(p);
|
||||||
|
}
|
||||||
|
contours.push_back(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
std::stringstream msg;
|
||||||
|
msg << "GetContoursInPlane3D: found " << contours.size() << " contours:\n";
|
||||||
|
|
||||||
|
for(auto c : contours) {
|
||||||
|
msg << " Contour: \n";
|
||||||
|
for(auto p : c) {
|
||||||
|
msg << " " << p.x << " " << p.y << " \n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IFCImporter::LogInfo(msg.str().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return contours;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::vector<IfcVector2>> GetContoursInPlane(std::shared_ptr<TempMesh> mesh,IfcMatrix3 planeSpace,
|
||||||
|
IfcVector3 planeNor,IfcFloat planeOffset,
|
||||||
|
IfcVector3 extrusionDir,IfcVector3& wall_extrusion,bool& first) {
|
||||||
|
|
||||||
|
if(mesh->mVertcnt.size() == 1)
|
||||||
|
{
|
||||||
|
bool ok;
|
||||||
|
auto contour = GetContourInPlane2D(mesh,planeSpace,planeNor,planeOffset,extrusionDir,wall_extrusion,first,ok);
|
||||||
|
if(ok)
|
||||||
|
return std::vector<std::vector<IfcVector2>> {contour};
|
||||||
|
else
|
||||||
|
return std::vector<std::vector<IfcVector2>> {};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return GetContoursInPlane3D(mesh,planeSpace,planeOffset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
bool TryAddOpenings_Poly2Tri(const std::vector<TempOpening>& openings,const std::vector<IfcVector3>& nors,
|
bool TryAddOpenings_Poly2Tri(const std::vector<TempOpening>& openings,
|
||||||
TempMesh& curmesh)
|
TempMesh& curmesh)
|
||||||
{
|
{
|
||||||
IFCImporter::LogWarn("forced to use poly2tri fallback method to generate wall openings");
|
IFCImporter::LogWarn("forced to use poly2tri fallback method to generate wall openings");
|
||||||
|
@ -1498,61 +1780,41 @@ bool TryAddOpenings_Poly2Tri(const std::vector<TempOpening>& openings,const std:
|
||||||
try {
|
try {
|
||||||
|
|
||||||
ClipperLib::Clipper clipper_holes;
|
ClipperLib::Clipper clipper_holes;
|
||||||
size_t c = 0;
|
|
||||||
|
|
||||||
for(const TempOpening& t :openings) {
|
for(const TempOpening& t : openings) {
|
||||||
const IfcVector3& outernor = nors[c++];
|
auto contours = GetContoursInPlane(t.profileMesh,m,nor,coord,t.extrusionDir,wall_extrusion,first);
|
||||||
const IfcFloat dot = nor * outernor;
|
|
||||||
if (std::fabs(dot)<1.f-1e-6f) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::vector<IfcVector3>& va = t.profileMesh->mVerts;
|
for(auto& contour : contours) {
|
||||||
if(va.size() <= 2) {
|
// scale to clipping space
|
||||||
continue;
|
ClipperLib::Polygon hole;
|
||||||
}
|
for(IfcVector2& pip : contour) {
|
||||||
|
pip.x = (pip.x - vmin.x) / vmax.x;
|
||||||
|
pip.y = (pip.y - vmin.y) / vmax.y;
|
||||||
|
|
||||||
std::vector<IfcVector2> contour;
|
hole.push_back(ClipperLib::IntPoint(to_int64(pip.x),to_int64(pip.y)));
|
||||||
|
|
||||||
for(const IfcVector3& xx : t.profileMesh->mVerts) {
|
|
||||||
IfcVector3 vv = m * xx, vv_extr = m * (xx + t.extrusionDir);
|
|
||||||
|
|
||||||
const bool is_extruded_side = std::fabs(vv.z - coord) > std::fabs(vv_extr.z - coord);
|
|
||||||
if (first) {
|
|
||||||
first = false;
|
|
||||||
if (dot > 0.f) {
|
|
||||||
wall_extrusion = t.extrusionDir;
|
|
||||||
if (is_extruded_side) {
|
|
||||||
wall_extrusion = - wall_extrusion;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// XXX should not be necessary - but it is. Why? For precision reasons?
|
if(!ClipperLib::Orientation(hole)) {
|
||||||
vv = is_extruded_side ? vv_extr : vv;
|
std::reverse(hole.begin(),hole.end());
|
||||||
contour.push_back(IfcVector2(vv.x,vv.y));
|
// assert(ClipperLib::Orientation(hole));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*ClipperLib::Polygons pol_temp(1), pol_temp2(1);
|
||||||
|
pol_temp[0] = hole;
|
||||||
|
|
||||||
|
ClipperLib::OffsetPolygons(pol_temp,pol_temp2,5.0);
|
||||||
|
hole = pol_temp2[0];*/
|
||||||
|
|
||||||
|
clipper_holes.AddPolygon(hole,ClipperLib::ptSubject);
|
||||||
|
{
|
||||||
|
std::stringstream msg;
|
||||||
|
msg << "- added polygon ";
|
||||||
|
for(auto elem : hole) {
|
||||||
|
msg << " (" << elem.X << ", " << elem.Y << ")";
|
||||||
|
}
|
||||||
|
IFCImporter::LogDebug(msg.str().c_str());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ClipperLib::Polygon hole;
|
|
||||||
for(IfcVector2& pip : contour) {
|
|
||||||
pip.x = (pip.x - vmin.x) / vmax.x;
|
|
||||||
pip.y = (pip.y - vmin.y) / vmax.y;
|
|
||||||
|
|
||||||
hole.push_back(ClipperLib::IntPoint( to_int64(pip.x), to_int64(pip.y) ));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ClipperLib::Orientation(hole)) {
|
|
||||||
std::reverse(hole.begin(), hole.end());
|
|
||||||
// assert(ClipperLib::Orientation(hole));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*ClipperLib::Polygons pol_temp(1), pol_temp2(1);
|
|
||||||
pol_temp[0] = hole;
|
|
||||||
|
|
||||||
ClipperLib::OffsetPolygons(pol_temp,pol_temp2,5.0);
|
|
||||||
hole = pol_temp2[0];*/
|
|
||||||
|
|
||||||
clipper_holes.AddPolygon(hole,ClipperLib::ptSubject);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
clipper_holes.Execute(ClipperLib::ctUnion,holes_union,
|
clipper_holes.Execute(ClipperLib::ctUnion,holes_union,
|
||||||
|
|
|
@ -307,7 +307,6 @@ void ProcessBooleanExtrudedAreaSolidDifference(const Schema_2x3::IfcExtrudedArea
|
||||||
// IFCOpenings.cpp
|
// IFCOpenings.cpp
|
||||||
|
|
||||||
bool GenerateOpenings(std::vector<TempOpening>& openings,
|
bool GenerateOpenings(std::vector<TempOpening>& openings,
|
||||||
const std::vector<IfcVector3>& nors,
|
|
||||||
TempMesh& curmesh,
|
TempMesh& curmesh,
|
||||||
bool check_intersection,
|
bool check_intersection,
|
||||||
bool generate_connection_geometry,
|
bool generate_connection_geometry,
|
||||||
|
|
|
@ -388,15 +388,16 @@ void DefaultLogger::WriteToStreams(const char *message, ErrorSeverity ErrorSev)
|
||||||
ai_assert(nullptr != message);
|
ai_assert(nullptr != message);
|
||||||
|
|
||||||
// Check whether this is a repeated message
|
// Check whether this is a repeated message
|
||||||
if (!::strncmp(message, lastMsg, lastLen - 1)) {
|
auto thisLen = ::strlen(message);
|
||||||
|
if (thisLen == lastLen - 1 && !::strncmp(message, lastMsg, lastLen - 1)) {
|
||||||
if (!noRepeatMsg) {
|
if (!noRepeatMsg) {
|
||||||
noRepeatMsg = true;
|
noRepeatMsg = true;
|
||||||
message = "Skipping one or more lines with the same contents\n";
|
message = "Skipping one or more lines with the same contents\n";
|
||||||
} else
|
}
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
// append a new-line character to the message to be printed
|
// append a new-line character to the message to be printed
|
||||||
lastLen = ::strlen(message);
|
lastLen = thisLen;
|
||||||
::memcpy(lastMsg, message, lastLen + 1);
|
::memcpy(lastMsg, message, lastLen + 1);
|
||||||
::strcat(lastMsg + lastLen, "\n");
|
::strcat(lastMsg + lastLen, "\n");
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue