diff --git a/code/IFCLoader.cpp b/code/IFCLoader.cpp index 1b60d4162..1010a4a9b 100644 --- a/code/IFCLoader.cpp +++ b/code/IFCLoader.cpp @@ -1097,16 +1097,116 @@ void ProcessSweptAreaSolid(const IFC::IfcSweptAreaSolid& swept, TempMesh& meshou } // ------------------------------------------------------------------------------------------------ -void ProcessBoolean(const IFC::IfcBooleanResult& boolean, TempMesh& meshout, ConversionData& conv) +enum Intersect { + Intersect_No, + Intersect_LiesOnPlane, + Intersect_Yes +}; + +// ------------------------------------------------------------------------------------------------ +Intersect IntersectSegmentPlane(const aiVector3D& p,const aiVector3D& n, const aiVector3D& e0, const aiVector3D& e1, aiVector3D& out) +{ + const aiVector3D pdelta = e0 - p, seg = e1-e0; + const float dotOne = n*seg, dotTwo = -(n*pdelta); + + if (fabs(dotOne) < 1e-6) { + return fabs(dotTwo) < 1e-6f ? Intersect_LiesOnPlane : Intersect_No; + } + + const float t = dotTwo/dotOne; + // 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; + } + out = e0+t*seg; + return Intersect_Yes; +} + +// ------------------------------------------------------------------------------------------------ +void ProcessBoolean(const IFC::IfcBooleanResult& boolean, TempMesh& result, ConversionData& conv) { if(const IFC::IfcBooleanClippingResult* const clip = boolean.ToPtr()) { + if(clip->Operator != "DIFFERENCE") { + IFCImporter::LogWarn("encountered unsupported boolean operator: " + (std::string)clip->Operator); + return; + } + + TempMesh meshout; + const IFC::IfcHalfSpaceSolid* const hs = clip->SecondOperand->ResolveSelectPtr(conv.db); + if(!hs) { + IFCImporter::LogError("expected IfcHalfSpaceSolid as second clipping operand"); + return; + } + + const IFC::IfcPlane* const plane = hs->BaseSurface->ToPtr(); + if(!plane) { + IFCImporter::LogError("expected IfcPlane as base surface for the IfcHalfSpaceSolid"); + return; + } + if(const IFC::IfcBooleanResult* const op0 = clip->FirstOperand->ResolveSelectPtr(conv.db)) { ProcessBoolean(*op0,meshout,conv); } else if (const IFC::IfcSweptAreaSolid* const swept = clip->FirstOperand->ResolveSelectPtr(conv.db)) { ProcessSweptAreaSolid(*swept,meshout,conv); - // XXX } + else { + IFCImporter::LogError("expected IfcSweptAreaSolid or IfcBooleanResult as first clipping operand"); + return; + } + + // extract plane base position vector and normal vector + aiVector3D p,n(0.f,0.f,1.f); + if (plane->Position->Axis) { + ConvertDirection(n,plane->Position->Axis.Get()); + } + ConvertCartesianPoint(p,plane->Position->Location); + + if(!IsTrue(hs->AgreementFlag)) { + n *= -1.f; + } + + // clip the current contents of `meshout` against the plane we obtained from the second operand + const std::vector& in = meshout.verts; + std::vector& outvert = result.verts; + std::vector::const_iterator outer_polygon = meshout.vertcnt.end(), begin=meshout.vertcnt.begin(), iit; + + unsigned int vidx = 0; + for(iit = begin; iit != meshout.vertcnt.end(); vidx += *iit++) { + + unsigned int newcount = 0; + for(unsigned int i = 0; i < *iit; ++i) { + const aiVector3D& e0 = in[vidx+i], e1 = in[vidx+(i+1)%*iit]; + + // does the next segment intersect the plane? + aiVector3D 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 ) { + // e0 is on the right side, so keep it + outvert.push_back(e0); + outvert.push_back(isectpos); + newcount += 2; + } + else { + // e0 is on the wrong side, so drop it and keep e1 instead + outvert.push_back(isectpos); + ++newcount; + } + } + } + + if(newcount) { + result.vertcnt.push_back(newcount); + } + } + IFCImporter::LogDebug("generating CSG geometry by plane clipping (IfcBooleanClippingResult)"); } else { IFCImporter::LogWarn("skipping unknown IfcBooleanResult entity, type is " + boolean.GetClassName()); diff --git a/code/IFCReaderGen.cpp b/code/IFCReaderGen.cpp index 86927ad04..86fc9f69e 100644 --- a/code/IFCReaderGen.cpp +++ b/code/IFCReaderGen.cpp @@ -1278,8 +1278,7 @@ template <> size_t GenericFill(const DB& db, const LIST& template <> size_t GenericFill(const DB& db, const LIST& params, IfcSurface* in) { size_t base = GenericFill(db,params,static_cast(in)); -// this data structure is not used yet, so there is no code generated to fill its members - return base; + if (params.GetSize() < 0) { throw STEP::TypeError("expected 0 arguments to IfcSurface"); } return base; } // ----------------------------------------------------------------------------------------------------------- template <> size_t GenericFill(const DB& db, const LIST& params, IfcBoundedSurface* in) @@ -1312,7 +1311,18 @@ template <> size_t GenericFill(const DB& db, const LIST& params template <> size_t GenericFill(const DB& db, const LIST& params, IfcHalfSpaceSolid* in) { size_t base = GenericFill(db,params,static_cast(in)); -// this data structure is not used yet, so there is no code generated to fill its members + if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcHalfSpaceSolid"); } do { // convert the 'BaseSurface' argument + const DataType* arg = params[base++]; + if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } + try { GenericConvert( in->BaseSurface, *arg, db ); break; } + catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcHalfSpaceSolid to be a `IfcSurface`")); } + } while(0); + do { // convert the 'AgreementFlag' argument + const DataType* arg = params[base++]; + if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } + try { GenericConvert( in->AgreementFlag, *arg, db ); break; } + catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcHalfSpaceSolid to be a `BOOLEAN`")); } + } while(0); return base; } // ----------------------------------------------------------------------------------------------------------- @@ -1557,15 +1567,19 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcC template <> size_t GenericFill(const DB& db, const LIST& params, IfcElementarySurface* in) { size_t base = GenericFill(db,params,static_cast(in)); -// this data structure is not used yet, so there is no code generated to fill its members + if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcElementarySurface"); } do { // convert the 'Position' argument + const DataType* arg = params[base++]; + if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } + try { GenericConvert( in->Position, *arg, db ); break; } + catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcElementarySurface to be a `IfcAxis2Placement3D`")); } + } while(0); return base; } // ----------------------------------------------------------------------------------------------------------- template <> size_t GenericFill(const DB& db, const LIST& params, IfcPlane* in) { size_t base = GenericFill(db,params,static_cast(in)); -// this data structure is not used yet, so there is no code generated to fill its members - return base; + if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcPlane"); } return base; } // ----------------------------------------------------------------------------------------------------------- template <> size_t GenericFill(const DB& db, const LIST& params, IfcCostSchedule* in) diff --git a/scripts/IFCImporter/entitylist.txt b/scripts/IFCImporter/entitylist.txt index d9aa5a602..a212acd8a 100644 --- a/scripts/IFCImporter/entitylist.txt +++ b/scripts/IFCImporter/entitylist.txt @@ -8,6 +8,8 @@ # code generator. Also, the names of all used entities need to be present # in the source code for this to work. +IfcPlane +IfcHalfSpaceSolid IfcAxis1Placement IfcMeasureWithUnit IfcConversionBasedUnit